Machine/JVM

코드 캐시(Code Cache) 사이즈 조정을 통한 JVM 최적화

코드 캐시 사이즈와 C2 컴파일러

앞선 글 에서 JVM은 성능 최적화를 위해 자주 사용되는 코드에 대해 C2 Compiler 을 사용하여 바이트 코드를 변환한 기계어를 캐시해놓고, 다음에 같은 코드가 실행되면 캐시에서 가져오는 방식으로 실행 시킨다고 하였다.  

*이러한 방식으로 컴파일을 수행하는 컴파일러가 바로 JIT 컴파일러이며, 캐시 해놓은 코드를 가져오는 것은 마치 미리 컴파일 해놓은 코드를 가져오는 방식과 비슷하게 되어 실행 속도가 비약적으로 빨라진다. 

 

JVM의 JIT(Just In Time Compilation) 컴파일이란? C1, C2 컴파일러를 이용한 최적화

JVM에서 코드를 실행하는 방법 JVM은 바이트 코드(Byte Code)라 불리는 기계어 코드를 실행하는 가상 머신이다. 우리가 Java나 Kotlin으로 코드를 작성하면 코드들은 컴파일 타임에 바이트 코드로 변환

kotlinworld.com

 

하지만 만약 어플리케이션이 커지게 되어 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

 

 

 

반응형

 

이 글의 저작권은 Dev World 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com