코드 캐시 사이즈와 C2 컴파일러
앞선 글 에서 JVM은 성능 최적화를 위해 자주 사용되는 코드에 대해 C2 Compiler 을 사용하여 바이트 코드를 변환한 기계어를 캐시해놓고, 다음에 같은 코드가 실행되면 캐시에서 가져오는 방식으로 실행 시킨다고 하였다.
*이러한 방식으로 컴파일을 수행하는 컴파일러가 바로 JIT 컴파일러이며, 캐시 해놓은 코드를 가져오는 것은 마치 미리 컴파일 해놓은 코드를 가져오는 방식과 비슷하게 되어 실행 속도가 비약적으로 빨라진다.
하지만 만약 어플리케이션이 커지게 되어 Code를 캐시해놓을 수 있는 충분한 메모리 공간이 없다면 C2 컴파일러는 더이상 효율적으로 동작하지 못한다. 따라서 이때는 Code Cache Size를 확인하고 Tuning 해야 한다. 이번 글에서는 java 버전 별로 Code Cache Size가 어떻게 되는지와 함께 Code Cache Size를 Tuning 하는 방법을 다루고자 한다.
JVM의 코드 캐시 사이즈 확인하기
java 실행 옵션에 -XX:+PrintCodeCache을 추가함으로써 코드 캐시 사이즈를 확인할 수 있다. 예를 들어 Main.class를 돌리고 싶다면 다음과 같이 사용하면 된다.
$ java -XX:+PrintCodeCache Main
hello
CodeCache: size=245760Kb used=1250Kb max_used=1450Kb free= 244510Kb
bounds [0x000000011489d000, 0x0000000114b0d000, 0x000000012389d000]
total_blobs=298 nmethods=40 adapters=172
compilation: enabled
위의 코드는 java8이 기준으로, java9보다 높은 버전에서는 Tiered Compilation이 적용되어 아래와 같이 나온다.
$ java -XX:+PrintCodeCache Main
hello
CodeHeap 'non-profiled nmethods': size=120032Kb used=16Kb max_used=16Kb free=120015Kb
bounds [0x000000011d5f8000, 0x000000011d868000, 0x0000000124b30000]
CodeHeap 'profiled nmethods': size=120028Kb used=113Kb max_used=113Kb free=119914Kb
bounds [0x00000001160c1000, 0x0000000116331000, 0x000000011d5f8000]
CodeHeap 'non-nmethods': size=5700Kb used=973Kb max_used=982Kb free=4726Kb
bounds [0x0000000115b30000, 0x0000000115da0000, 0x00000001160c1000]
total_blobs=318 nmethods=86 adapters=146
compilation: enabled
stopped_count=0, restarted_count=0
full_count=0
java11 기준으로 위와 같이 CodeHeap(Code Cache)이 3가지 공간으로 나오며, 이는 Tiered Compiliation 옵션이 생겨서 그렇다. Tiered Compilation은 method의 종류에 따라 다르게 컴파일하는 옵션이다.
만약 위의 Tiered Compilation을 끄고 싶다면 -XX:-TieredCompilation 을 적용하면 된다. -XX는 옵션에 대한 적용을 뜻하며 그 뒤에 -가 붙는지, +가 붙는지에 따라 옵션이 켜지고 꺼진다. 따라서 -XX:-TieredCompilation 을 적용하면 TieredCompilation 옵션이 꺼져서 아래와 같이 나온다. java8과 똑같이 나오는 것을 확인할 수 있다.
java -XX:+PrintCodeCache -XX:-TieredCompilation Main
hello
CodeCache: size=49152Kb used=382Kb max_used=385Kb free=48769Kb
bounds [0x000000011687e000, 0x0000000116aee000, 0x000000011987e000]
total_blobs=196 nmethods=1 adapters=146
compilation: enabled
stopped_count=0, restarted_count=0
full_count=0
위의 실행 결과를 통해 java 버전 별로 Code Cache Size가 다르게 나오는 것을 볼 수 있다. java8을 기준으로는 default Code Cache Size가 24MB로 나오는 것을 볼 수 있다. 이는 java7이전의 잔재로, java7이전의 최대 코드 캐시 사이즈는 64bit JVM을 기준으로 48MB 밖에 안되어서, default 값이 24MB으로 잡힌다. 반대로 java11을 기준으로 3가지 CodeHeap(CodeCache) Size를 더해보면 240MB정도가 나오는 것을 볼 수 있다.
java 버전 별로 Code Cache Size가 다르게 나오는 이유는 java8을 기준으로 default 코드 캐시 사이즈가 늘어났으며, java9보다 높은 버전을 기준으로 jvm의 성능 향상을 위해 코드 캐시의 공간을 늘리는 방향으로 jvm이 개선되었기 때문이다.
JVM 종류별 Code Cache Size
기본 Code Cache Size
위에서 java 버전 별로 코드 캐시 사이즈의 기본값이 변경되었다고 했다. 변경된 값은 다음과 같다.
jdk + JVM 종류 | default Code Cache Size |
java8 보다 낮거나 같은 버전 + 32bit JVM | 32MB |
java8 보다 낮거나 같은 버전 + 64bit JVM | 48MB |
java9 보다 높거나 같은 버전 | 240MB |
실제로 48MB 정도의 Code Cache Size로는 큰 어플리케이션에서 충분한 최적화가 불가능하며, default Code Cache Size가 java8부터는 240MB로 바뀌었다.
최대 Code Cache Size
java8의 64bit JVM을 기준으로 240MB가 최대 Code Cache Size이며, java9보다 높은 버전을 기준으로 최대로 설정할 수 있는 Code Cache Size는 2GB이다.
jdk + JVM 종류 | 최대 Code Cache Size |
java8 보다 낮거나 같은 버전 + 32bit JVM | 48MB |
java8 보다 낮거나 같은 버전 + 64bit JVM | 240MB |
java9 보다 높거나 같은 버전 | 2GB |
JVM Code Cache Tuning 하기
위의 코드 캐시 사이즈를 튜닝하기 위해서는 다음 세가지 옵션을 줄 수 있다.
- -XX:InitialCodeCacheSize : 시작 시 코드 캐시 사이즈
- -XX:ReservedCodeCacheSize : 코드 캐시 사이즈 최대치
- -XX:CodeCacheExpansionSize : 코드 캐시 사이즈가 늘어나는 단위 설정(현재 코드 캐시 사이즈가 다 차면 이만큼씩 늘어난다)
단위는 다음과 같다.
- k : 킬로바이트
- m : 메가바이트
- g : 기가바이트
예를 들어 -XX:InitialCodeCacheSize=240m -XX:ReservedCodeCacheSize=2g 두가지 옵션을 줘서 코드 캐시 사이즈를 설정하면 초기 코드 캐시 사이즈가 240M 이고, 이 값이 2GB까지 늘어날 수 있게 된다.
$ java -XX:+PrintCodeCache -XX:InitialCodeCacheSize=240m -XX:ReservedCodeCacheSize=2g Main
hello
CodeHeap 'non-profiled nmethods': size=1045728Kb used=19Kb max_used=19Kb free=1045708Kb
bounds [0x000000015665c000, 0x000000016565c000, 0x0000000196394000]
CodeHeap 'profiled nmethods': size=1045724Kb used=109Kb max_used=109Kb free=1045614Kb
bounds [0x0000000116925000, 0x0000000125925000, 0x000000015665c000]
CodeHeap 'non-nmethods': size=5700Kb used=982Kb max_used=991Kb free=4717Kb
bounds [0x0000000116394000, 0x0000000116925000, 0x0000000116925000]
total_blobs=316 nmethods=84 adapters=146
compilation: enabled
stopped_count=0, restarted_count=0