[JVM] JIT 컴파일 로그, 컴파일 스레드, 인라이닝 튜닝
JVM의 PrintCompilation 옵션을 활성화시켜서 컴파일 로그를 확인하자
그리고 컴파일 되어야 하는 코드가 예상대로 컴파일 되고 있는지 확인하자
- 메서드나 루프가 컴파일될 때마다 컴파일된 대상에 대한 정보를 출력한다
[timestamp compilation_id attributes (tiered_level) method_name size deopt]
- timestamp : JVM 시작시간을 0으로 했을 때, 컴파일이 끝난 시간
- attributes : 컴파일되고 있는 코드의 상태를 기호로 나타낸다.
1) % : OSR 컴파일을 의미
2) s : 메소드가 동기화됨
3) ! : 메소드는 예외 핸들러를 가지고 있다
4) b : 블로킹 모드에서 컴파일된다 (컴파일이 백그라운드에서 실행되지 않는다)
5) n : 네이티브 메서드를 호출하기 위해 컴파일된다
- teired_level : 티어드 컴파일일 경우, 컴파일이 완료된 Tier(-server, -client)가 무엇인지 나타낸다.
- size : 자바 바이트 코드 크기. 컴파일된 코드의 크기가 아니다. 따라서 코드 캐시크기를 예측하기 위해 사용될 수 없다.
- deopt : 컴파일할 때 발생한 역최적화(deoptimization)에 대한 정보. 역최적화에는 "not entrant", "made zombie"와 같은 정보가 자주 나타난다. subclass가 로드되어서 메소드가 오버라이드된다면 기존에 컴파일된 코드는 호출될 수 없다. 이런 코드를 "not entrant"라고 한다. 핫스팟은 코드를 인터프리터로 다시 돌리고 컴파일에 필요한 정보를 다시 모은다. "not entrant" 인 코드가 계속 남아있는 경우 "made zombie" 라고 한다.
컴파일 임계치에 도달한 메소드나 루프는 컴파일 큐에 들어간다.
큐는 한 개 이상의 백그라운드 컴파일 스레드에 의해 처리된다
컴파일은 비동기적으로 실행되며, 대상 코드가 컴파일 중인 동안에도 프로그램이 실행될 수 있다
OSR의 경우 루프는 컴파일 된 후에 바로 다음 반복부터 컴파일된 코드를 실행한다.
큐는 우선순위가 적용되는 큐이다
클라이언트 컴파일러의 경우 JVM은 한 개의 컴파일 스레드로 시작해서 증가한다.
서버 컴파일러의 경우 두 개의 컴파일 스레드로 시작한다.
컴파일러 스레드의 개수를 조정한다.
JVM이 컴파일 큐를 처리하는데 사용되는 전체 스레드의 개수를 의미한다
즉, 티어드일 경우 1/3은 클라이언트 컴파일러 큐를 처리하는데 이용되고 나머지는 서버 컴파일러 큐를 처리하는데 사용된다. 서버와 클라이언트 컴파일러의 최소 값은 1개이다.
클라이언트, 또는 티어드 컴파일일 경우 초기에 컴파일이 많이 일어난다. 이때 컴파일 스레드의 개수를 늘리게 되면 사용자의 CPU를 많이 잡아먹어서 어플리케이션이 느려질 수 있다. 이 경우 컴파일 스레드의 수를 줄이는 것이 좋다.
컴파일 스레드의 수가 적절해서 컴파일이 빠르게 된다고 해도, 크게 이득볼 일은 없다. 그저 컴파일이 되는 시간만 빠른 것이다. 다만 큐 안에 코드가 늘어날 수록 어플리케이션이 컴파일된 코드를 실행하여 최적의 성능을 이루는데 시간이 오래걸린다.
인라이닝이 가장 많이 일어나는 곳은 getter / setter
Ex. 위의 코드는 컴파일러가 인라이닝을 통해 아래와 같이 컴파일된다.
Point p = getPoint();
p.setX( p.getX() * 2);
====================
Point p = getPoint();
p.x = p.x*2;
디폴트로 활성화되어 있다.
반드시 사용해야 할 정도로 성능 향상효과를 보여준다.
JVM이 인라이닝을 적용하는 상황을 모니터링 하는 방법은 없지만,
JVM 소스를 컴파일 할 때 -XX:+PrintInlining 옵션을 주면 디버그 버전을 만들 수 있다.
-XX:MaxInlineSize=N (default : 35bytes)
인라이닝이 적용되는 메소드는 빈도와 크기로 결정된다.
자주호출되는 메소드가 MaxFreqInlineSize 보다 작다면 인라이닝한다
자주호출되지 않는 메소드의 크기가 MaxInlineSize 보다 작다면 인라이닝한다
★ 인라이닝 튜닝은 거의 할 필요 없다.
※ final 키워드를 사용하면 성능이 좋아진다?
- final 키워드가 JIT 컴파일러의 인라이닝과 그 외의 최적화 적용 여부를 결정하는 성능상 중요한 요소라고 여겨진다. 옛날부터 그렇게 믿어왔지만 진실이 아님이 밝혀졌다! 좀처럼 사라지지 않는 유언비어!
final 키워드를 사용했다고 해서 성능상 이점을 갖는것은 아니다!
서버 컴파일러를 사용하면 컴파일 관련 성능 이슈의 90%는 해결된다.
고려할만한 것은, 코드 캐시의 크기를 충분히 크게 잡을 것
'JAVA > JVM' 카테고리의 다른 글
[JVM] Throughput Collector - 힙 사이즈 튜닝하기 (0) | 2021.12.16 |
---|---|
[JVM] Throughput Collector - GC Log (0) | 2021.12.16 |
[JVM] 기본 GC 튜닝 (0) | 2021.12.16 |
[JVM] GC 알고리즘 - Overview (0) | 2021.12.16 |
[JVM] JIT : Just In Time Compiler 개념 & 튜닝 (0) | 2021.12.16 |
[JVM] PERM 영역 이해하기 (0) | 2021.12.16 |
[JVM] CPU 사용률 , Run Queue 런큐 (0) | 2021.12.16 |
[JVM] Heap dump 힙 덤프 개념 - 왜 필요한가 (0) | 2021.12.16 |