티스토리 뷰
들어가는 말
몇 년 전 elasitcsearch 클러스터를 새로 구성했을 때 heap 관련 설정으로 찾았던 내용을 기록한다.
기본적으로는 ES 공식 가이드 (구버전)의 내용을 따라간다.
요약
jvm 기반의 애플리케이션이라면 heap 최대 사이즈를 32GB를 넘기지 말도록 고려하라
테스트해보고 30~31GB 중 적당한 사이즈를 사용하자
배경
시간이 좀 지났지만, elasitcsearch 클러스터를 새롭게 구성하던 중 여러 가지 튜닝 포인트들을 찾아서 적용하였다.
그중 가장 재미있던 내용이 '32GB 이상으로 힙을 설정하지 마라'이다.
jvm 기반의 애플리케이션을 다루다 보면 매번 고민하게 되는 문제가 있다. STW.
heap을 많이 잡는다면 성능이 잘 나올 것 같은 느낌이 있지만 그렇지 않다.
full gc 가 돌기 시작한다면 애플리케이션은 먹통이 된다.
독립적으로 구성된 애플리케이션이라면 큰 문제가 안될 수도 있다. (물론 HA구성은 잘해둬야 한다.)
클러스터로 구성되는 컴포넌트들에서는 피곤한 결과를 이끌어 낼 수도 있다. (설정을 잘해두면 피할 수 있긴 하다.)
어떤 노드가 full gc 도는 동안, 클러스터를 구성하는 다른 노드들에서는 해당 노드와 통신을 할 수 없으므로 해당 노드가 클러스터를 이탈한 것으로 판단한다.
한 노드가 클러스터에서 빠졌으니 자기들끼리 이리 복구하고, 저리 복구하는 과정을 거치며 '남은 노드들'에 부하가 걸린다.
full gc가 끝나면 '미안, 나 다시 왔어, 너네들 뭐 때문에 그렇게 바빠?' 하는 상황이 발생할 수 있다.
문제는 잠시 이탈했던 노드가 돌아오기 전에 '남은 노드들'에서도 full gc 가 걸릴 수 있다는 것이다.
heap을 무작정 많이 잡으면 안 되는데, 하필 ~32GB로 잡으라고 가이드하는 것인가.
OOPS (Ordinary Object Pointer)
java는 포인터가 없다.
이 말에는 여러 가지 의미가 함축되어 있다... 모든 게 포인터이다.라는 표현도 납득이 간다.
jvm 은 오브젝트의 레퍼런스를 관리하기 위해 oops(ordinary object pointers)라고 불리는 자료구조를 가진다.
(자세한 내용은 공부가 더 필요하다. 키워드: instanceOops, Object Memory Layout)
32bit 시스템(ILP32)에서 oops 는 최대 힙을 4GB (2^32)까지 사용할 수 있다.
64bit 시스템(LP64)에서 oops 는 최대 힙을 18.5EB (2^64)까지 사용할 수 있다.
다만 64bit 포인터로 공간을 관리하는 것은 매우 비효율 적이다.
(기존까지는 힙을 많이 잡으면 STW 시간이 증가한다. 그 정도로 큰 메모리는 서버에 안 꽂혀있다.)
Compressed OOPS
java에서는 트리키 한 방법을 사용했다.
Compressed OOPS를 통해 32bit 만으로도 32GB 힙을 사용할 수 있도록
기존에는 oops가 4GB (2^32) 만큼 메모리 영역을 직접 관리했다.
Compressed OOPS에서는 oops 가 object를 referencing 하는 대신 object의 offset을 referencing 한다.
3bit만큼을 더 사용할 수 있으므로 64bit 레퍼런스가 아닐 때에도 2^32 (4GB) * 2^3 (8) = 32GB만큼의 힙을 사용할 수 있다.
원문 (This means that we can use up to 32 GB – 2^32+3=2^35=32 GB – of heap space without using 64-bit references.)
다만, 32GB를 넘게 되면 기존의 oops를 사용하게 된다. (64bit 시스템)
확인은 간단하다
Xmx 를 31G로 설정한 경우, UseCompressedOops 옵션이 켜져 있다.
$ java -Xmx31G -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version 2>/dev/null | grep 'UseCompressedOops'
bool UseCompressedOops = true
{lp64_product} {ergonomic}
Xmx 를 32G로 설정한 경우, UseCompressedOops 옵션이 꺼져있다.
$ java -Xmx32G -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version 2>/dev/null | grep 'UseCompressedOops'
bool UseCompressedOops = false
{lp64_product} {default}
Zero-based Compressed OOPS
그 외에 다른 분의 블로그를 보다 재미있는 사항을 확인하였다.
compress oops 가 활성화되어있다고 해도 zero based 가 아니면 성능상 손해를 본다는 내용이었다.
jvm 위키에 포함되어 있는 내용을 완벽하게 이해한 것은 아니지만,
zero based oops 가 아닌 경우에는 base 주소로부터 실제 오브젝트의 주소를 찾기 위해 더하기 연산이 필요하지만, zero based oops 일 때에는 더하기 연산이 필요하지 않아 쉬프트 연산만으로 오브젝트의 레퍼런스를 구할 수 있다는 내용이다.
확인했던 방법만 첨부한다.
30G일때에는 Compressed Oops mode: Zero based라는 문구를 확인했지만 31G일 때에는 그렇지 않다.
compressed oops 가 활성화되지 않는 32G 부터는 메시지가 뜨지 않는다.
$ java -Xmx30G -XX:+UnlockDiagnosticVMOptions -Xlog:gc+heap+coops=debug -version
[0.009s][info][gc,heap,coops] Heap address: 0x0000000080000000, size: 30720 MB, Compressed Oops mode: Zero based, Oop shift amount: 3
$ java -Xmx31G -XX:+UnlockDiagnosticVMOptions -Xlog:gc+heap+coops=debug -version
[0.003s][debug][gc,heap,coops] Protected page at the reserved heap base: 0x0000001000000000 / 8388608 bytes
[0.010s][info ][gc,heap,coops] Heap address: 0x0000001000800000, size: 31744 MB, Compressed Oops mode: Non-zero disjoint base: 0x0000001000000000, Oop shift amount: 3
나가는 말
32GB를 최대로 하라고 하지만 그것보다 더 적게 잡아야 할 것 같다.
메모리를 한정적으로 사용할 수 있으므로 한 서버에 2대 노드를 올리는 구성도 가능하다. (고려사항이 있다. 무작정 쓰면 안 된다.)
java의 Object Memory Layout 을 완벽하게 이해하지 못한 채로 전개하다 보니 납득이 잘 안 가는 부분이 있다.
레퍼런스
www.baeldung.com/jvm-compressed-oops
www.elastic.co/guide/en/elasticsearch/guide/master/heap-sizing.html
dev-punxism.tistory.com/entry/JVM-OOPSOrdinary-object-pointer
wiki.openjdk.java.net/display/HotSpot/CompressedOops
'개발관련 > java' 카테고리의 다른 글
PECS - java generic (0) | 2021.05.08 |
---|---|
java 버전 별 인터페이스 메소드 (0) | 2021.05.05 |
- Total
- Today
- Yesterday
- jhipster
- 기술블로그
- elasticsearch
- 기술사이트
- percolate
- COMMIT
- Dangling
- 전설로떠나는월가의영웅
- 개발자
- meta character
- 클린 아키텍처
- Kafka
- Generic
- 기술센싱
- completablefuture
- WebSocket
- 만들면서 배우는 클린 아키텍처
- 에픽테토스
- opensearch
- PatternSyntaxException
- AWS
- flush
- 사기꾼증후군
- 말의품격
- kafka 2.8.0
- Spring
- fsync
- Async
- pecs
- Java
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |