SlideShare a Scribd company logo
1 of 43
Download to read offline
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

More Related Content

What's hot

【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践
【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践
【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践日本マイクロソフト株式会社
 
[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-
[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-
[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-Amazon Web Services Japan
 
Advanced backup methods (Postgres@CERN)
Advanced backup methods (Postgres@CERN)Advanced backup methods (Postgres@CERN)
Advanced backup methods (Postgres@CERN)Anastasia Lubennikova
 
슬라이드쉐어
슬라이드쉐어슬라이드쉐어
슬라이드쉐어sungminlee
 
AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...
AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...
AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...Amazon Web Services
 
【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)
【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)
【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)日本マイクロソフト株式会社
 
Amazon Redshift의 이해와 활용 (김용우) - AWS DB Day
Amazon Redshift의 이해와 활용 (김용우) - AWS DB DayAmazon Redshift의 이해와 활용 (김용우) - AWS DB Day
Amazon Redshift의 이해와 활용 (김용우) - AWS DB DayAmazon Web Services Korea
 
AWS LambdaとDynamoDBがこんなにツライはずがない #ssmjp
AWS LambdaとDynamoDBがこんなにツライはずがない #ssmjpAWS LambdaとDynamoDBがこんなにツライはずがない #ssmjp
AWS LambdaとDynamoDBがこんなにツライはずがない #ssmjpMasahiro NAKAYAMA
 
PHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache Kafka
PHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache KafkaPHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache Kafka
PHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache KafkaSzymonSadlo
 
AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100
AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100
AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100Amazon Web Services Korea
 
Getting started with Amazon ElastiCache
Getting started with Amazon ElastiCacheGetting started with Amazon ElastiCache
Getting started with Amazon ElastiCacheAmazon Web Services
 
파이썬 생존 안내서 (자막)
파이썬 생존 안내서 (자막)파이썬 생존 안내서 (자막)
파이썬 생존 안내서 (자막)Heungsub Lee
 
How to build massive service for advance
How to build massive service for advanceHow to build massive service for advance
How to build massive service for advanceDaeMyung Kang
 
Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017
Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017
Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017Amazon Web Services Korea
 
大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)
大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)
大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)NTT DATA Technology & Innovation
 
AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...
AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...
AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...Amazon Web Services Korea
 
[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들
[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들
[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들NAVER D2
 
AWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWS
AWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWSAWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWS
AWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWSAmazon Web Services Korea
 

What's hot (20)

Azure IoT Edge入門
Azure IoT Edge入門Azure IoT Edge入門
Azure IoT Edge入門
 
【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践
【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践
【de:code 2020】 Azure Red hat OpenShift (ARO) によるシステムアーキテクチャ構築の実践
 
[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-
[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-
[AWSマイスターシリーズ] AWS Elastic Beanstalk -Python編-
 
Advanced backup methods (Postgres@CERN)
Advanced backup methods (Postgres@CERN)Advanced backup methods (Postgres@CERN)
Advanced backup methods (Postgres@CERN)
 
슬라이드쉐어
슬라이드쉐어슬라이드쉐어
슬라이드쉐어
 
AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...
AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...
AWS re:Invent 2016: Netflix: Using Amazon S3 as the fabric of our big data ec...
 
【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)
【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)
【de:code 2020】 Azure Synapse Analytics 技術編 ~ 最新の統合分析プラットフォームによる新しい価値の創出(後編)
 
At least onceってぶっちゃけ問題の先送りだったよね #kafkajp
At least onceってぶっちゃけ問題の先送りだったよね #kafkajpAt least onceってぶっちゃけ問題の先送りだったよね #kafkajp
At least onceってぶっちゃけ問題の先送りだったよね #kafkajp
 
Amazon Redshift의 이해와 활용 (김용우) - AWS DB Day
Amazon Redshift의 이해와 활용 (김용우) - AWS DB DayAmazon Redshift의 이해와 활용 (김용우) - AWS DB Day
Amazon Redshift의 이해와 활용 (김용우) - AWS DB Day
 
AWS LambdaとDynamoDBがこんなにツライはずがない #ssmjp
AWS LambdaとDynamoDBがこんなにツライはずがない #ssmjpAWS LambdaとDynamoDBがこんなにツライはずがない #ssmjp
AWS LambdaとDynamoDBがこんなにツライはずがない #ssmjp
 
PHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache Kafka
PHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache KafkaPHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache Kafka
PHP w kolejce, kolejka w PHP - jak wykorzystać moc Apache Kafka
 
AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100
AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100
AWS와 함께하는 클라우드 컴퓨팅 - 강철, AWS 어카운트 매니저 :: AWS Builders 100
 
Getting started with Amazon ElastiCache
Getting started with Amazon ElastiCacheGetting started with Amazon ElastiCache
Getting started with Amazon ElastiCache
 
파이썬 생존 안내서 (자막)
파이썬 생존 안내서 (자막)파이썬 생존 안내서 (자막)
파이썬 생존 안내서 (자막)
 
How to build massive service for advance
How to build massive service for advanceHow to build massive service for advance
How to build massive service for advance
 
Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017
Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017
Amazon Aurora 성능 향상 및 마이그레이션 모범 사례 - AWS Summit Seoul 2017
 
大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)
大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)
大規模データ活用向けストレージレイヤソフトのこれまでとこれから(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)
 
AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...
AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...
AWS상에서 블록체인 서비스 구축 및 활용가이드 대방출! - 박천구 솔루션즈 아키텍트, AWS / 오재훈 이사, 두나무 :: AWS Sum...
 
[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들
[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들
[115]쿠팡 서비스 클라우드 마이그레이션 통해 배운것들
 
AWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWS
AWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWSAWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWS
AWS에서 빅데이터 프로젝트 시작하기 - 이종화 솔루션즈 아키텍트, AWS
 

Similar to 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
 

Similar to 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