SlideShare une entreprise Scribd logo
1  sur  43
Télécharger pour lire hors ligne
Greg’s Anatomy
JVM 메모리 해부학
오픈서베이 이동훈(a.k.a Greg)
greg.lee@opensurvey.co.kr
leewin12@gmail.com
최초작성: 2020-07-18
외부공개: 2020-09-23
배경
- 평화롭게 오픈서베이의 신규 데이터 분석 서비스인 OpenAnalytics 를
개발하던 어느날, QA 과정에서 OutOfMemory 이슈가 등장함.
- 이건 과거의 내가 미래의 나에게 뭔가 잘못한 것이 분명했음.
- 과거의 나를 회상해보는 시간을 잠시 가져봄.
- (과거의 나) 설문 응답 데이터가 커봐야 얼마나 크겠어
- 많아봐야 Long / String 이백만건 정도인데,
String은 별로 없으니, Long만 따졌을 때, 8 * 2_000_000 = 16 MB
많이 봐줘서 10배 쳐줘도 160 MB니 -Xmx6g (= Heap 6GB)면 메모린 남아돌겠지?
- 역시 과거의 나는 믿을게 별로 못 된다는 사실부터 재확인
Java Object
new Integer(1)
Q. JVM 상에서 위 Object의 크기는? (JVM 32 bit 가정)
A. 16 bytes(= 128 bits)
- 네, 그렇습니다. 16 byte * 8 = 128 bit
- 잠깐, 그런데, 실제 값은 고작 4 Byte고 나머진 뭔가요?
- 그리고 Retained Size와 Shallow Size는 또 뭔가요?
- Shallow Size는 객체 자체가 점유하고 있는 메모리 크기
- Retained Size는 직접 GC Root와 연결되지 않고, Shallow를 통해 간접 ref된 크기
코드로 추적해봅시다.
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
public native int hashCode(); ← Retained Size (2)
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws
InterruptedException;
}
public final class Integer extends Number implements
Comparable<Integer> {
private final int value; ← Shallow Size (4)
}
public abstract class Number implements
java.io.Serializable {}
[주1] 코드 추적은 어디까지나 제 추측입니다. 틀린 부분 있으면 알려주세요
코드로 추적해봅시다.
/*
* jdk/src/share/native/java/lang/Object.c
*/
static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode}, ← 2
{"wait", "(J)V", (void *)&JVM_MonitorWait}, ← 3
{"notify", "()V", (void *)&JVM_MonitorNotify}, ← 3
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll}, ← 3
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass
cls) {
(*env)->RegisterNatives(env, cls, methods,
sizeof(methods)/sizeof(methods[0]));
}
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this) {
….
return (*env)->GetObjectClass(env, this); ← 1
}
이하 중략...
(결론적) Integer Object의 구성[1]
- 실제 primitive value 대비 Object의 크기 비율은 3:1 = 4배
0 32 64 96 128 160
(1) Class
Pointer
(2) Flags (3) Locks (5) size (4) int...
0 32 64 96 128
(1) Class
Pointer
(2) Flags (3) Locks (4) int
- Array, (5) size가 추가됨
- 실제 primitive value 대비 Object의 크기 비율은 4:1 = 5배 (최악 가정)
(1) Class pointer
- Class Type의 Memory Address
- 따라서, 당연히 JVM의 bit 버전에 영향을 받음.
- e.g. 윈도우 XP (x86)의 최대 인식 메모리는 최대 4G 였음
- (참고사항) JVM 64bit에 도입된 -XX:-UseCompressedOops -XX:-UseCompressedClassPointer 등에 영향 받음
- Oops: ordinary object pointer
- 오픈서베이 표준 JVM인 Zulu8은 기본적으로 두 옵션이 모두 true로 켜져있음
- Oracle JVM에서는 false로 꺼져있음
- 자세한 사항은 https://wiki.openjdk.java.net/display/HotSpot/CompressedOops
- A collection of flags that describe the state of the object,
including the hash code for the object if it has one, and the shape
of the object (that is, whether or not the object is an array) [1]
- 하지만 실제로, 코드상으로 확인되는 항목은 hashcode 뿐으로,
크기를 봤을 때 그 이상의 추가적인 flag가 존재하긴 어려워보임
- 뇌피셜입니다. (= 인터넷 문서상에서는 위처럼 기술되어 있지만, JDK 코드상에서 추가적
정보를 찾지 못함.)
(2) Flags
(3) Locks
- The synchronization information for the object — that is, whether
the object is currently synchronized [1]
- ()V로 표시되던, wait / notify 등 동기화 관련 상태 필드
(4) 타입별 크기 (Oracle JDK8 Spec, 32 bit)[2]
- Primitive 한 경우
- boolean 혼란[4]
- 첫 스펙 문서에서 크기를 정확히 지정하지 않았음.
- 4 bytes (Oracle JDK8 Spec: Where Java programming language boolean values are mapped by compilers to values
of Java Virtual Machine type int, the compilers must use the same encoding.) [2]
- 1 bytes (Zulu8)
- byte 1 byte
- char 2 bytes ← byte < char 크기차 주의, low-level 처리시 실수 포인트
- short 2 bytes
- int 4 bytes
- long 8 bytes
- float 4 bytes
- double 8 bytes
- Non-Primitive 한 경우
- Object 16 bytes
- String ? bytes
new String(6)
Q. JVM 상에서 위 Object의 크기는? (JVM 32 bit 가정)
A. 24 bytes + char array
0 32 64 96 128 160 192 224
(1) Class
Pointer
(2) Flags (3) Locks (4) char
Array
Pointer
(5) hash
(1) Class
Pointer
(2) Flags (3) Locks (4) size 1 / 2 3 / 4 5 / 6
- String은 내부에 다시 character array 와 hash로 구성
확인해봅시다 by JOLJava Object Layout [3]
$ java -jar jol-cli-0.11-full.jar internals java.lang.String
java.lang.String object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) da 02 00 f8 (11011010 00000010 00000000 11111000) (-134216998)
12 4 char[] String.value []
16 4 int String.hash 0
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
얼추 맞아 보이죠?
그런데 말입니다…
코드로 추적해봅시다. Zulu8 (JDK8)
코드로 추적해봅시다. AdoptOpenJ9 (JDK8)
- int count가 있네요?
코드로 추적해봅시다. AdoptOpenJ9 (JDK11)
- char[] value → byte[] value
- byte count → byte coder ??
코드로 추적해봅시다. OpenJDK14 (JDK14)
- coder: encoding을 표현하는 필드
- int hashcode → int hash (아무리 Compile time에 이름을 제거한다지만, 이런걸 줄이냐...)
코드로 추적해봅시다. (결론)
- 디테일은 JDK Vender와 JDK Version에 의해 다르다.
(참고) String.intern
- String은 JVM에서 대우가 남다른 Type으로서, 별도의 저장영역 존재
- String.intern을 통해 JVM character pool에 Cache됨 (HashTable)
- String `==`와 `equals`에 주의해야하는 이유
Java Collection
Java Collection
- List
- ArrayList
- LinkedList
- Map
- HashMap
- LinkedHashMap
- TreeMap
- Set
- HashSet
- LinkedHashSet
- HashMap
ArrayList
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (5)
4 4 (object header) 00 00 00 00 (0)
8 4 (object header) 7e 2f 00 f8 (-134200)
12 4 int AbstractList.modCount 0
16 4 int ArrayList.size 0
20 4 java.lang.Object[] ArrayList.elementData []
Instance size: 24 bytes
LinkedList
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (5)
4 4 (object header) 00 00 00 00 (0)
8 4 (object header) 19 af 00 f8 (-134172903)
12 4 int AbstractList.modCount 0
16 4 int LinkedList.size 0
20 4 java.util.LinkedList.Node LinkedList.first null
24 4 java.util.LinkedList.Node LinkedList.last null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
생각과 달리 크기가 작지 않습니다.
LinkedList
- LinkedList$Node
- Object item
- Node next
- Node prev
- Node first
- Node last
- int size
- Elements의 수에 비례해서 급격한 차이
- LinkedList 사용을 지양하고,
ArrayList.trimToSize() 사용권고
- new ArrayList()의 기본 크기 10이며,
크기를 초과할 경우, 임의의 크기 만큼
확장함
- 따라서, 실제 element 갯수보다 더 큰
크기를 확보하고 있을 가능성이 높음.
- trimToSize()는 이러한 Array 상의 null
element 제거해줌
그래프 출처: Numeron,
https://stackoverflow.com/a/7671021/1378965
ArrayList vs LinkedList
HashMap
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) (5)
4 4 (object header) (0)
8 4 (object header) (-134203459)
12 4 java.util.Set AbstractMap.keySet null
16 4 java.util.Collection AbstractMap.values null
20 4 int HashMap.size 0
24 4 int HashMap.modCount 0
28 4 int HashMap.threshold 0
32 4 float HashMap.loadFactor 0.75
36 4 java.util.HashMap.Node[] HashMap.table null
40 4 java.util.Set HashMap.entrySet null
44 4 (loss due to the next object alignment)
Instance size: 48 bytes
HashMap
- HashMap$Node
- int hash
- Object key
- Object value
- Node next
- Node[] table
- Set entrySet
- int size
- int modCount
- int threshold
- int loadFactor
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) (5)
4 4 (object header) (0)
8 4 (object header) (-134195647)
12 4 java.util.Set AbstractMap.keySet null
16 4 java.util.Collection AbstractMap.values null
24 4 int HashMap.modCount 0
20 4 int HashMap.size 0
28 4 int HashMap.threshold 0
32 4 float HashMap.loadFactor 0.75
36 4 java.util.HashMap.Node[] HashMap.table null
40 4 java.util.Set HashMap.entrySet null
44 1 boolean LinkedHashMap.accessOrder false
45 3 (alignment/padding gap)
48 4 java.util.LinkedHashMap.Entry LinkedHashMap.head null
52 4 java.util.LinkedHashMap.Entry LinkedHashMap.tail null
Instance size: 56 bytes
LinkedHashMap
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) (5)
4 4 (object header) (0)
8 4 (object header) (-134179429)
12 4 java.util.Set AbstractMap.keySet null
16 4 java.util.Collection AbstractMap.values null
20 4 int TreeMap.size 0
24 4 int TreeMap.modCount 0
28 4 java.util.Comparator TreeMap.comparator null
32 4 java.util.TreeMap.Entry TreeMap.root null
36 4 java.util.TreeMap.EntrySet TreeMap.entrySet null
40 4 java.util.TreeMap.KeySet TreeMap.navigableKeySet null
44 4 java.util.NavigableMap TreeMap.descendingMap null
Instance size: 48 bytes
TreeMap
Set
사실 Java의 Set은 Map으로 구현되어 있음
정말 Wrapper임 (= 직관적으로 기대되는 메모리 상 이득은 전혀 없음)
$ java -jar jol-cli-0.11-full.jar internals java.util.HashSet
java.util.HashSet object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) (5)
4 4 (object header) (0)
8 4 (object header) (-134177913)
12 4 java.util.HashMap HashSet.map (object)
Instance size: 16 bytes
개선해보기
개선해보기
- JVM의 Object는 생각보다 무겁다.
- 적절한 자료구조를 선택하기
- Queue가 필요한 것이 아닌 이상, LinkedList는 쓰지 말자.
- Set은 Map만큼 무겁다.
- 필요한 만큼만 사용하기
- ArrayList.trimToSize() : List 상의 null element 최소화
- new HashMap(7)
- JVM paramater 도입 -XX:+UseCompressOops
- Zulu8에선 이미 기본 적용사항임 https://chriswhocodes.com/zulu_jdk8_options.html
- 하지만, Oracle JDK8에서는 기본 값이 false임
- JVM parameter계의 explainshell 인 JaCoLine 도 겸사겸사 추천
개선해보기(cont.)
- Eclipse Collections
- Primitive Collection 구현체들 중 가장 잘 유지보수 되고 있는 오픈소스
- 메모리 최적화된 Set 구현체도 보유
- 하지만, 항상 그렇듯이 먼저 문서를 확인하시고, 프로젝트 별로 벤치마크를
권고드립니다. 요즘 JMH라는 좋은 툴이 생겼어요. (하지만 JDK8은 추가작업이…)
- 더 알아보기
- https://www.infoq.com/articles/eclipse-collections/
- https://www.infoq.com/articles/Refactoring-to-Eclipse-Collections/
개선해보기(cont.)
Eclipse Collections 도입 PoC 결과 비교(1)
- 믿기 어려울 정도의 개선효과
- 십수회 반복 측정 결과이지만, 그래도 뭔가 잘못 측정된 것이 아닐까?
구현방법 소요시간 Obj 갯수
최대 메모리 사용량
(Peak지점 사용량)
종료기점
Stron Reachable
Object 크기
JDK 기본 1,428,126 ms 224 M 6.4 Gb 3.6Gb
Eclipse Collections
503,218 ms
(65% 감소)
123 M
(45% 감소)
4.8 Gb
(25% 감소)
2.3 Gb
(34% 감소)
개선해보기(cont.)
메모리 상 Object 구성 분석 thanks to yourkit
- (상) JDK 기본
- (하) EclipseCollections
Eclipse Collections 도입 PoC 결과 비교 (2)
개선해보기(cont.)
● 메모리 상 Object 구성(2)이 크게 달라진 것을 확인 할 수 있음
○ EC.Immutable*List의 특징
■ List가 Imuutable 하다는 점을 이용하여 다양한 최적화가 적용되어있음
■ List 크기 불변성을 이용한 null element 수 최소화
■ ImmutableSingleList부터 Decapleton까지 element 갯수에 따른 micro tuning
● Immutable 자료구조를 통해 기존 Defensive Copy 정책으로 인한 중복 Object 생성비용 절감
● 메모리 상 Object 구성(2)에서 Object[]로 표현되던, 각종 자료 구조(ArrayList, Set)
내부에서 참조 중인 Object를 Primitive로 다이어트
● (퍼포먼스 상 부가이득) Boxing / Unboxing 비용절약
● (퍼포먼스 상 부가이득) 효율적인 메모리 활용을 통한 GC 소요시간 단축
결론: OpenAnalytics의 구조적 특징과 Primitive 자료구조의 시너지 효과
Eclipse Collections 도입 PoC 결과 비교 (3)
Q. 그렇다면 다른 프로젝트에서도 이런 드라마틱한 효과를 기대할 수 있을까요?
A. 그럴 수도 있고 아닐 수도 있습니다. 전술한 바와같이, OpenAnalytics의 드라마틱한 개선효과는
전적으로 OpenAnalytics의 구조적 특징과 Primitive 자료구조의 시너지 효과입니다.
본 자료는 Eclipse Collections 도입시, 성능 향상 가능성을 판단하는데,
도움을 줄 수 있는 사례 하나라고 생각합니다.
도입에 앞서서 충분한 검토와 Benchmark를 권고합니다.
개선해보기(cont.)
Eclipse Collections 도입 PoC 결과 비교 (4)
[없을 수도 있는 차회예고]
VTL: 오픈서베이에서 개발한 설문분석 전용 DSL 개발기
Shell pipe 스타일의 집합 연산 언어
끝!
참고문헌
[1] https://www.ibm.com/developerworks/java/library/j-codetoheap/index.html
[2] https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.3
[3] https://openjdk.java.net/projects/code-tools/jol/
[4] https://stackoverflow.com/questions/383551/what-is-the-size-of-a-boolean-variable-in-java
부록: JOL
JOL-cli https://repo.maven.apache.org/maven2/org/openjdk/jol/jol-cli/0.11/jol-cli-0.11-full.jar
$ java -jar jol-cli-0.11-full.jar
Usage: jol-cli.jar <mode> [optional arguments]*
Available modes:
estimates: Simulate the class layout in different VM modes.
externals: Show the object externals: the objects reachable from a given instance.
footprint: Estimate the footprint of all objects reachable from a given instance
heapdump: Consume the heap dump and estimate the savings in different layout strategies.
heapdumpstats: Consume the heap dump and print the most frequent instances.
idealpack: Compute the object footprint under different field layout strategies.
internals: Show the object internals: field layout and default contents, object header
shapes: Dump the object shapes present in JAR files or heap dumps.
string-compress: Consume the heap dumps and figures out the savings attainable with compressed strings.
적용해보기: http://www.mastertheboss.com/jboss-server/jboss-monitoring/monitoring-the-size-of-your-java-objects-with-java-object-layout
com.idincu.analytics2.ast.value.ValueRow object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) (13)
4 4 (object header) (0)
8 4 (object header) (-133007067)
12 4 java.util.Set AbstractMap.keySet null
16 4 java.util.Collection AbstractMap.values null
20 4 int HashMap.size 48
24 4 int HashMap.modCount 48
28 4 int HashMap.threshold 48
32 4 float HashMap.loadFactor 0.75
36 4 java.util.HashMap.Node[] HashMap.table [(object), null, ... ]
40 4 java.util.Set HashMap.entrySet null
44 4 (loss due to the next object alignment)
Instance size: 48 bytes
부록: JOL

Contenu connexe

Tendances

Evaluating Real-Time Anomaly Detection: The Numenta Anomaly Benchmark
Evaluating Real-Time Anomaly Detection: The Numenta Anomaly BenchmarkEvaluating Real-Time Anomaly Detection: The Numenta Anomaly Benchmark
Evaluating Real-Time Anomaly Detection: The Numenta Anomaly BenchmarkNumenta
 
Photo-realistic Single Image Super-resolution using a Generative Adversarial ...
Photo-realistic Single Image Super-resolution using a Generative Adversarial ...Photo-realistic Single Image Super-resolution using a Generative Adversarial ...
Photo-realistic Single Image Super-resolution using a Generative Adversarial ...Hansol Kang
 
Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...
Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...
Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...Simplilearn
 
How to Test for The OWASP Top Ten
 How to Test for The OWASP Top Ten How to Test for The OWASP Top Ten
How to Test for The OWASP Top TenSecurity Innovation
 
Machine Learning in Cyber Security
Machine Learning in Cyber SecurityMachine Learning in Cyber Security
Machine Learning in Cyber SecurityRishi Kant
 
Perceptron 2015.ppt
Perceptron 2015.pptPerceptron 2015.ppt
Perceptron 2015.pptSadafAyesha9
 
GANs Presentation.pptx
GANs Presentation.pptxGANs Presentation.pptx
GANs Presentation.pptxMAHMOUD729246
 
Research of adversarial example on a deep neural network
Research of adversarial example on a deep neural networkResearch of adversarial example on a deep neural network
Research of adversarial example on a deep neural networkNAVER Engineering
 
[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptx
[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptx[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptx
[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptxChi En (Ashley) Shen
 
Machine Learning - Challenges, Learnings & Opportunities
Machine Learning - Challenges, Learnings & OpportunitiesMachine Learning - Challenges, Learnings & Opportunities
Machine Learning - Challenges, Learnings & OpportunitiesCodePolitan
 
Dziesięć Przykazań Prezentacja
Dziesięć Przykazań PrezentacjaDziesięć Przykazań Prezentacja
Dziesięć Przykazań PrezentacjaKffiatek
 
IBM-QRadar-Corporate-Online-Training.
IBM-QRadar-Corporate-Online-Training.IBM-QRadar-Corporate-Online-Training.
IBM-QRadar-Corporate-Online-Training.Avishek Priyadarshi
 
What is security testing and why it is so important?
What is security testing and why it is so important?What is security testing and why it is so important?
What is security testing and why it is so important?ONE BCG
 
Focal loss for dense object detection
Focal loss for dense object detectionFocal loss for dense object detection
Focal loss for dense object detectionDaeHeeKim31
 
Hands on machine learning with scikit-learn and tensor flow by ahmed yousry
Hands on machine learning with scikit-learn and tensor flow by ahmed yousryHands on machine learning with scikit-learn and tensor flow by ahmed yousry
Hands on machine learning with scikit-learn and tensor flow by ahmed yousryAhmed Yousry
 
Bug Bounty - Play For Money
Bug Bounty - Play For MoneyBug Bounty - Play For Money
Bug Bounty - Play For MoneyShubham Gupta
 

Tendances (20)

Evaluating Real-Time Anomaly Detection: The Numenta Anomaly Benchmark
Evaluating Real-Time Anomaly Detection: The Numenta Anomaly BenchmarkEvaluating Real-Time Anomaly Detection: The Numenta Anomaly Benchmark
Evaluating Real-Time Anomaly Detection: The Numenta Anomaly Benchmark
 
Photo-realistic Single Image Super-resolution using a Generative Adversarial ...
Photo-realistic Single Image Super-resolution using a Generative Adversarial ...Photo-realistic Single Image Super-resolution using a Generative Adversarial ...
Photo-realistic Single Image Super-resolution using a Generative Adversarial ...
 
Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...
Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...
Deep Learning Tutorial | Deep Learning Tutorial For Beginners | What Is Deep ...
 
How to Test for The OWASP Top Ten
 How to Test for The OWASP Top Ten How to Test for The OWASP Top Ten
How to Test for The OWASP Top Ten
 
Machine Learning in Cyber Security
Machine Learning in Cyber SecurityMachine Learning in Cyber Security
Machine Learning in Cyber Security
 
Perceptron 2015.ppt
Perceptron 2015.pptPerceptron 2015.ppt
Perceptron 2015.ppt
 
GANs Presentation.pptx
GANs Presentation.pptxGANs Presentation.pptx
GANs Presentation.pptx
 
AlexNet.pptx
AlexNet.pptxAlexNet.pptx
AlexNet.pptx
 
Como ser-um-jovem-segundo-o-coração-de-deus
Como ser-um-jovem-segundo-o-coração-de-deusComo ser-um-jovem-segundo-o-coração-de-deus
Como ser-um-jovem-segundo-o-coração-de-deus
 
Research of adversarial example on a deep neural network
Research of adversarial example on a deep neural networkResearch of adversarial example on a deep neural network
Research of adversarial example on a deep neural network
 
[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptx
[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptx[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptx
[HITCON 2020 CTI Village] Threat Hunting and Campaign Tracking Workshop.pptx
 
Machine Learning - Challenges, Learnings & Opportunities
Machine Learning - Challenges, Learnings & OpportunitiesMachine Learning - Challenges, Learnings & Opportunities
Machine Learning - Challenges, Learnings & Opportunities
 
Dziesięć Przykazań Prezentacja
Dziesięć Przykazań PrezentacjaDziesięć Przykazań Prezentacja
Dziesięć Przykazań Prezentacja
 
Deep Learning for Computer Vision: Object Detection (UPC 2016)
Deep Learning for Computer Vision: Object Detection (UPC 2016)Deep Learning for Computer Vision: Object Detection (UPC 2016)
Deep Learning for Computer Vision: Object Detection (UPC 2016)
 
IBM-QRadar-Corporate-Online-Training.
IBM-QRadar-Corporate-Online-Training.IBM-QRadar-Corporate-Online-Training.
IBM-QRadar-Corporate-Online-Training.
 
What is security testing and why it is so important?
What is security testing and why it is so important?What is security testing and why it is so important?
What is security testing and why it is so important?
 
Focal loss for dense object detection
Focal loss for dense object detectionFocal loss for dense object detection
Focal loss for dense object detection
 
Hands on machine learning with scikit-learn and tensor flow by ahmed yousry
Hands on machine learning with scikit-learn and tensor flow by ahmed yousryHands on machine learning with scikit-learn and tensor flow by ahmed yousry
Hands on machine learning with scikit-learn and tensor flow by ahmed yousry
 
Bug Bounty - Play For Money
Bug Bounty - Play For MoneyBug Bounty - Play For Money
Bug Bounty - Play For Money
 
Deep Learning for Computer Vision: Medical Imaging (UPC 2016)
Deep Learning for Computer Vision: Medical Imaging (UPC 2016)Deep Learning for Computer Vision: Medical Imaging (UPC 2016)
Deep Learning for Computer Vision: Medical Imaging (UPC 2016)
 

Similaire à JVM 메모리 해부학

Scala, Scalability
Scala, ScalabilityScala, Scalability
Scala, ScalabilityDongwook Lee
 
Collection framework
Collection frameworkCollection framework
Collection frameworkssuser34b989
 
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)MIN SEOK KOO
 
Java advancd ed10
Java advancd ed10Java advancd ed10
Java advancd ed10hungrok
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8Sangmin Lee
 
Programming java day2
Programming java day2Programming java day2
Programming java day2Jaehoonyam
 
자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라Jong Gook Bae
 
Java mentoring of samsung scsc 2
Java mentoring of samsung scsc   2Java mentoring of samsung scsc   2
Java mentoring of samsung scsc 2도현 김
 
From Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&CFrom Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&Csys4u
 
XECon + PHPFest 2014 Keynote
XECon + PHPFest 2014 KeynoteXECon + PHPFest 2014 Keynote
XECon + PHPFest 2014 KeynoteSol Kim
 
자바로 배우는 자료구조
자바로 배우는 자료구조자바로 배우는 자료구조
자바로 배우는 자료구조중선 곽
 
ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!WooYoung Cho
 
Java 변수자료형
Java 변수자료형Java 변수자료형
Java 변수자료형Hyosang Hong
 
Java_02 변수자료형
Java_02 변수자료형Java_02 변수자료형
Java_02 변수자료형Hong Hyo Sang
 

Similaire à JVM 메모리 해부학 (20)

Scala, Scalability
Scala, ScalabilityScala, Scalability
Scala, Scalability
 
Scalability
ScalabilityScalability
Scalability
 
Collection framework
Collection frameworkCollection framework
Collection framework
 
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
 
Java advancd ed10
Java advancd ed10Java advancd ed10
Java advancd ed10
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8
 
Programming java day2
Programming java day2Programming java day2
Programming java day2
 
Gpg1
Gpg1Gpg1
Gpg1
 
자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라
 
Java mentoring of samsung scsc 2
Java mentoring of samsung scsc   2Java mentoring of samsung scsc   2
Java mentoring of samsung scsc 2
 
From Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&CFrom Java code to Java heap_SYS4U I&C
From Java code to Java heap_SYS4U I&C
 
XECon + PHPFest 2014 Keynote
XECon + PHPFest 2014 KeynoteXECon + PHPFest 2014 Keynote
XECon + PHPFest 2014 Keynote
 
ES6 for Node.js Study 4주차
ES6 for Node.js Study 4주차ES6 for Node.js Study 4주차
ES6 for Node.js Study 4주차
 
자바로 배우는 자료구조
자바로 배우는 자료구조자바로 배우는 자료구조
자바로 배우는 자료구조
 
4-1. javascript
4-1. javascript4-1. javascript
4-1. javascript
 
Scala
ScalaScala
Scala
 
ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!
 
Java 변수자료형
Java 변수자료형Java 변수자료형
Java 변수자료형
 
Java_02 변수자료형
Java_02 변수자료형Java_02 변수자료형
Java_02 변수자료형
 
JDK 변천사
JDK 변천사JDK 변천사
JDK 변천사
 

JVM 메모리 해부학

  • 1. Greg’s Anatomy JVM 메모리 해부학 오픈서베이 이동훈(a.k.a Greg) greg.lee@opensurvey.co.kr leewin12@gmail.com 최초작성: 2020-07-18 외부공개: 2020-09-23
  • 2. 배경 - 평화롭게 오픈서베이의 신규 데이터 분석 서비스인 OpenAnalytics 를 개발하던 어느날, QA 과정에서 OutOfMemory 이슈가 등장함. - 이건 과거의 내가 미래의 나에게 뭔가 잘못한 것이 분명했음. - 과거의 나를 회상해보는 시간을 잠시 가져봄. - (과거의 나) 설문 응답 데이터가 커봐야 얼마나 크겠어 - 많아봐야 Long / String 이백만건 정도인데, String은 별로 없으니, Long만 따졌을 때, 8 * 2_000_000 = 16 MB 많이 봐줘서 10배 쳐줘도 160 MB니 -Xmx6g (= Heap 6GB)면 메모린 남아돌겠지? - 역시 과거의 나는 믿을게 별로 못 된다는 사실부터 재확인
  • 4. new Integer(1) Q. JVM 상에서 위 Object의 크기는? (JVM 32 bit 가정)
  • 5. A. 16 bytes(= 128 bits) - 네, 그렇습니다. 16 byte * 8 = 128 bit - 잠깐, 그런데, 실제 값은 고작 4 Byte고 나머진 뭔가요? - 그리고 Retained Size와 Shallow Size는 또 뭔가요? - Shallow Size는 객체 자체가 점유하고 있는 메모리 크기 - Retained Size는 직접 GC Root와 연결되지 않고, Shallow를 통해 간접 ref된 크기
  • 6. 코드로 추적해봅시다. public class Object { private static native void registerNatives(); static { registerNatives(); } public native int hashCode(); ← Retained Size (2) public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException; } public final class Integer extends Number implements Comparable<Integer> { private final int value; ← Shallow Size (4) } public abstract class Number implements java.io.Serializable {} [주1] 코드 추적은 어디까지나 제 추측입니다. 틀린 부분 있으면 알려주세요
  • 7. 코드로 추적해봅시다. /* * jdk/src/share/native/java/lang/Object.c */ static JNINativeMethod methods[] = { {"hashCode", "()I", (void *)&JVM_IHashCode}, ← 2 {"wait", "(J)V", (void *)&JVM_MonitorWait}, ← 3 {"notify", "()V", (void *)&JVM_MonitorNotify}, ← 3 {"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll}, ← 3 {"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone}, Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])); } JNIEXPORT jclass JNICALL Java_java_lang_Object_getClass(JNIEnv *env, jobject this) { …. return (*env)->GetObjectClass(env, this); ← 1 } 이하 중략...
  • 8. (결론적) Integer Object의 구성[1] - 실제 primitive value 대비 Object의 크기 비율은 3:1 = 4배 0 32 64 96 128 160 (1) Class Pointer (2) Flags (3) Locks (5) size (4) int... 0 32 64 96 128 (1) Class Pointer (2) Flags (3) Locks (4) int - Array, (5) size가 추가됨 - 실제 primitive value 대비 Object의 크기 비율은 4:1 = 5배 (최악 가정)
  • 9. (1) Class pointer - Class Type의 Memory Address - 따라서, 당연히 JVM의 bit 버전에 영향을 받음. - e.g. 윈도우 XP (x86)의 최대 인식 메모리는 최대 4G 였음 - (참고사항) JVM 64bit에 도입된 -XX:-UseCompressedOops -XX:-UseCompressedClassPointer 등에 영향 받음 - Oops: ordinary object pointer - 오픈서베이 표준 JVM인 Zulu8은 기본적으로 두 옵션이 모두 true로 켜져있음 - Oracle JVM에서는 false로 꺼져있음 - 자세한 사항은 https://wiki.openjdk.java.net/display/HotSpot/CompressedOops
  • 10. - A collection of flags that describe the state of the object, including the hash code for the object if it has one, and the shape of the object (that is, whether or not the object is an array) [1] - 하지만 실제로, 코드상으로 확인되는 항목은 hashcode 뿐으로, 크기를 봤을 때 그 이상의 추가적인 flag가 존재하긴 어려워보임 - 뇌피셜입니다. (= 인터넷 문서상에서는 위처럼 기술되어 있지만, JDK 코드상에서 추가적 정보를 찾지 못함.) (2) Flags
  • 11. (3) Locks - The synchronization information for the object — that is, whether the object is currently synchronized [1] - ()V로 표시되던, wait / notify 등 동기화 관련 상태 필드
  • 12. (4) 타입별 크기 (Oracle JDK8 Spec, 32 bit)[2] - Primitive 한 경우 - boolean 혼란[4] - 첫 스펙 문서에서 크기를 정확히 지정하지 않았음. - 4 bytes (Oracle JDK8 Spec: Where Java programming language boolean values are mapped by compilers to values of Java Virtual Machine type int, the compilers must use the same encoding.) [2] - 1 bytes (Zulu8) - byte 1 byte - char 2 bytes ← byte < char 크기차 주의, low-level 처리시 실수 포인트 - short 2 bytes - int 4 bytes - long 8 bytes - float 4 bytes - double 8 bytes - Non-Primitive 한 경우 - Object 16 bytes - String ? bytes
  • 13. new String(6) Q. JVM 상에서 위 Object의 크기는? (JVM 32 bit 가정)
  • 14. A. 24 bytes + char array 0 32 64 96 128 160 192 224 (1) Class Pointer (2) Flags (3) Locks (4) char Array Pointer (5) hash (1) Class Pointer (2) Flags (3) Locks (4) size 1 / 2 3 / 4 5 / 6 - String은 내부에 다시 character array 와 hash로 구성
  • 15. 확인해봅시다 by JOLJava Object Layout [3] $ java -jar jol-cli-0.11-full.jar internals java.lang.String java.lang.String object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) da 02 00 f8 (11011010 00000010 00000000 11111000) (-134216998) 12 4 char[] String.value [] 16 4 int String.hash 0 Instance size: 24 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 얼추 맞아 보이죠? 그런데 말입니다…
  • 17. 코드로 추적해봅시다. AdoptOpenJ9 (JDK8) - int count가 있네요?
  • 18. 코드로 추적해봅시다. AdoptOpenJ9 (JDK11) - char[] value → byte[] value - byte count → byte coder ??
  • 19. 코드로 추적해봅시다. OpenJDK14 (JDK14) - coder: encoding을 표현하는 필드 - int hashcode → int hash (아무리 Compile time에 이름을 제거한다지만, 이런걸 줄이냐...)
  • 20. 코드로 추적해봅시다. (결론) - 디테일은 JDK Vender와 JDK Version에 의해 다르다.
  • 21. (참고) String.intern - String은 JVM에서 대우가 남다른 Type으로서, 별도의 저장영역 존재 - String.intern을 통해 JVM character pool에 Cache됨 (HashTable) - String `==`와 `equals`에 주의해야하는 이유
  • 23. Java Collection - List - ArrayList - LinkedList - Map - HashMap - LinkedHashMap - TreeMap - Set - HashSet - LinkedHashSet - HashMap
  • 24. ArrayList OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (5) 4 4 (object header) 00 00 00 00 (0) 8 4 (object header) 7e 2f 00 f8 (-134200) 12 4 int AbstractList.modCount 0 16 4 int ArrayList.size 0 20 4 java.lang.Object[] ArrayList.elementData [] Instance size: 24 bytes
  • 25. LinkedList OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (5) 4 4 (object header) 00 00 00 00 (0) 8 4 (object header) 19 af 00 f8 (-134172903) 12 4 int AbstractList.modCount 0 16 4 int LinkedList.size 0 20 4 java.util.LinkedList.Node LinkedList.first null 24 4 java.util.LinkedList.Node LinkedList.last null 28 4 (loss due to the next object alignment) Instance size: 32 bytes 생각과 달리 크기가 작지 않습니다.
  • 26. LinkedList - LinkedList$Node - Object item - Node next - Node prev - Node first - Node last - int size
  • 27. - Elements의 수에 비례해서 급격한 차이 - LinkedList 사용을 지양하고, ArrayList.trimToSize() 사용권고 - new ArrayList()의 기본 크기 10이며, 크기를 초과할 경우, 임의의 크기 만큼 확장함 - 따라서, 실제 element 갯수보다 더 큰 크기를 확보하고 있을 가능성이 높음. - trimToSize()는 이러한 Array 상의 null element 제거해줌 그래프 출처: Numeron, https://stackoverflow.com/a/7671021/1378965 ArrayList vs LinkedList
  • 28. HashMap OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) (5) 4 4 (object header) (0) 8 4 (object header) (-134203459) 12 4 java.util.Set AbstractMap.keySet null 16 4 java.util.Collection AbstractMap.values null 20 4 int HashMap.size 0 24 4 int HashMap.modCount 0 28 4 int HashMap.threshold 0 32 4 float HashMap.loadFactor 0.75 36 4 java.util.HashMap.Node[] HashMap.table null 40 4 java.util.Set HashMap.entrySet null 44 4 (loss due to the next object alignment) Instance size: 48 bytes
  • 29. HashMap - HashMap$Node - int hash - Object key - Object value - Node next - Node[] table - Set entrySet - int size - int modCount - int threshold - int loadFactor
  • 30. OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) (5) 4 4 (object header) (0) 8 4 (object header) (-134195647) 12 4 java.util.Set AbstractMap.keySet null 16 4 java.util.Collection AbstractMap.values null 24 4 int HashMap.modCount 0 20 4 int HashMap.size 0 28 4 int HashMap.threshold 0 32 4 float HashMap.loadFactor 0.75 36 4 java.util.HashMap.Node[] HashMap.table null 40 4 java.util.Set HashMap.entrySet null 44 1 boolean LinkedHashMap.accessOrder false 45 3 (alignment/padding gap) 48 4 java.util.LinkedHashMap.Entry LinkedHashMap.head null 52 4 java.util.LinkedHashMap.Entry LinkedHashMap.tail null Instance size: 56 bytes LinkedHashMap
  • 31. OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) (5) 4 4 (object header) (0) 8 4 (object header) (-134179429) 12 4 java.util.Set AbstractMap.keySet null 16 4 java.util.Collection AbstractMap.values null 20 4 int TreeMap.size 0 24 4 int TreeMap.modCount 0 28 4 java.util.Comparator TreeMap.comparator null 32 4 java.util.TreeMap.Entry TreeMap.root null 36 4 java.util.TreeMap.EntrySet TreeMap.entrySet null 40 4 java.util.TreeMap.KeySet TreeMap.navigableKeySet null 44 4 java.util.NavigableMap TreeMap.descendingMap null Instance size: 48 bytes TreeMap
  • 32. Set 사실 Java의 Set은 Map으로 구현되어 있음 정말 Wrapper임 (= 직관적으로 기대되는 메모리 상 이득은 전혀 없음) $ java -jar jol-cli-0.11-full.jar internals java.util.HashSet java.util.HashSet object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) (5) 4 4 (object header) (0) 8 4 (object header) (-134177913) 12 4 java.util.HashMap HashSet.map (object) Instance size: 16 bytes
  • 34. 개선해보기 - JVM의 Object는 생각보다 무겁다. - 적절한 자료구조를 선택하기 - Queue가 필요한 것이 아닌 이상, LinkedList는 쓰지 말자. - Set은 Map만큼 무겁다. - 필요한 만큼만 사용하기 - ArrayList.trimToSize() : List 상의 null element 최소화 - new HashMap(7) - JVM paramater 도입 -XX:+UseCompressOops - Zulu8에선 이미 기본 적용사항임 https://chriswhocodes.com/zulu_jdk8_options.html - 하지만, Oracle JDK8에서는 기본 값이 false임 - JVM parameter계의 explainshell 인 JaCoLine 도 겸사겸사 추천
  • 35. 개선해보기(cont.) - Eclipse Collections - Primitive Collection 구현체들 중 가장 잘 유지보수 되고 있는 오픈소스 - 메모리 최적화된 Set 구현체도 보유 - 하지만, 항상 그렇듯이 먼저 문서를 확인하시고, 프로젝트 별로 벤치마크를 권고드립니다. 요즘 JMH라는 좋은 툴이 생겼어요. (하지만 JDK8은 추가작업이…) - 더 알아보기 - https://www.infoq.com/articles/eclipse-collections/ - https://www.infoq.com/articles/Refactoring-to-Eclipse-Collections/
  • 36. 개선해보기(cont.) Eclipse Collections 도입 PoC 결과 비교(1) - 믿기 어려울 정도의 개선효과 - 십수회 반복 측정 결과이지만, 그래도 뭔가 잘못 측정된 것이 아닐까? 구현방법 소요시간 Obj 갯수 최대 메모리 사용량 (Peak지점 사용량) 종료기점 Stron Reachable Object 크기 JDK 기본 1,428,126 ms 224 M 6.4 Gb 3.6Gb Eclipse Collections 503,218 ms (65% 감소) 123 M (45% 감소) 4.8 Gb (25% 감소) 2.3 Gb (34% 감소)
  • 37. 개선해보기(cont.) 메모리 상 Object 구성 분석 thanks to yourkit - (상) JDK 기본 - (하) EclipseCollections Eclipse Collections 도입 PoC 결과 비교 (2)
  • 38. 개선해보기(cont.) ● 메모리 상 Object 구성(2)이 크게 달라진 것을 확인 할 수 있음 ○ EC.Immutable*List의 특징 ■ List가 Imuutable 하다는 점을 이용하여 다양한 최적화가 적용되어있음 ■ List 크기 불변성을 이용한 null element 수 최소화 ■ ImmutableSingleList부터 Decapleton까지 element 갯수에 따른 micro tuning ● Immutable 자료구조를 통해 기존 Defensive Copy 정책으로 인한 중복 Object 생성비용 절감 ● 메모리 상 Object 구성(2)에서 Object[]로 표현되던, 각종 자료 구조(ArrayList, Set) 내부에서 참조 중인 Object를 Primitive로 다이어트 ● (퍼포먼스 상 부가이득) Boxing / Unboxing 비용절약 ● (퍼포먼스 상 부가이득) 효율적인 메모리 활용을 통한 GC 소요시간 단축 결론: OpenAnalytics의 구조적 특징과 Primitive 자료구조의 시너지 효과 Eclipse Collections 도입 PoC 결과 비교 (3)
  • 39. Q. 그렇다면 다른 프로젝트에서도 이런 드라마틱한 효과를 기대할 수 있을까요? A. 그럴 수도 있고 아닐 수도 있습니다. 전술한 바와같이, OpenAnalytics의 드라마틱한 개선효과는 전적으로 OpenAnalytics의 구조적 특징과 Primitive 자료구조의 시너지 효과입니다. 본 자료는 Eclipse Collections 도입시, 성능 향상 가능성을 판단하는데, 도움을 줄 수 있는 사례 하나라고 생각합니다. 도입에 앞서서 충분한 검토와 Benchmark를 권고합니다. 개선해보기(cont.) Eclipse Collections 도입 PoC 결과 비교 (4)
  • 40. [없을 수도 있는 차회예고] VTL: 오픈서베이에서 개발한 설문분석 전용 DSL 개발기 Shell pipe 스타일의 집합 연산 언어 끝!
  • 41. 참고문헌 [1] https://www.ibm.com/developerworks/java/library/j-codetoheap/index.html [2] https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.3 [3] https://openjdk.java.net/projects/code-tools/jol/ [4] https://stackoverflow.com/questions/383551/what-is-the-size-of-a-boolean-variable-in-java
  • 42. 부록: JOL JOL-cli https://repo.maven.apache.org/maven2/org/openjdk/jol/jol-cli/0.11/jol-cli-0.11-full.jar $ java -jar jol-cli-0.11-full.jar Usage: jol-cli.jar <mode> [optional arguments]* Available modes: estimates: Simulate the class layout in different VM modes. externals: Show the object externals: the objects reachable from a given instance. footprint: Estimate the footprint of all objects reachable from a given instance heapdump: Consume the heap dump and estimate the savings in different layout strategies. heapdumpstats: Consume the heap dump and print the most frequent instances. idealpack: Compute the object footprint under different field layout strategies. internals: Show the object internals: field layout and default contents, object header shapes: Dump the object shapes present in JAR files or heap dumps. string-compress: Consume the heap dumps and figures out the savings attainable with compressed strings.
  • 43. 적용해보기: http://www.mastertheboss.com/jboss-server/jboss-monitoring/monitoring-the-size-of-your-java-objects-with-java-object-layout com.idincu.analytics2.ast.value.ValueRow object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) (13) 4 4 (object header) (0) 8 4 (object header) (-133007067) 12 4 java.util.Set AbstractMap.keySet null 16 4 java.util.Collection AbstractMap.values null 20 4 int HashMap.size 48 24 4 int HashMap.modCount 48 28 4 int HashMap.threshold 48 32 4 float HashMap.loadFactor 0.75 36 4 java.util.HashMap.Node[] HashMap.table [(object), null, ... ] 40 4 java.util.Set HashMap.entrySet null 44 4 (loss due to the next object alignment) Instance size: 48 bytes 부록: JOL