이 책을 읽게 된 이유는 전 직장의 팀장님이 이 책으로 스터디를 한 것이 계기가 되었다. 그 때 나는 막 자바를 업무에 사용하면서 이제 이 정도면 자바를 어느 정도 안다고 생각하고 있었다. 하지만 팀장님의 발표는 아예 처음 듣는 내용으로 가득했다. 그렇게 우매함의 봉우리에서 좌절을 맛보고 이 책은 언젠가는 꼭 읽어야 겠다고 생각하고 있었다.
역시나 예상했던 대로 책의 내용은 처음보는 내용들과 이해하기 힘든 내용들도 가득했다. 그래도 이왕 시작한 책을 이해가 가지 않더라도 끝까지 읽어보겠다고 다짐했고 최선을 다해서 이해했던 내용을 바탕으로 글을 써보겠다. 공부가 더 필요한 부분은 구글링을 통해서 내용을 찾아보며 정리했기에 구글링 내용도 함께 정리했다. 이 글은 독자가 자바를 어느 정도 사용을 해봤고 GC와 JIT에 관련해서 개괄적인 내용을 알고 있다는 가정을 가지고 작성했다.
개발자가 GC를 선정할 때는 모든 소프트웨어 엔지니어링이 그렇듯이 trade-off를 고려해야 한다. 대표적인 GC 선정시 고려해야 할 관점은 CPU 효율 및 처리율이 우수한 알고리즘을 선택할 지, 혹은 중단시간이 짧은 알고리즘을 선택할 지 이다. 최신 GC의 특징에 대해서 알아보고 어떤 상황에 어떤 GC를 선택해야 좋을지 알아보자.
최신 GC는 불확정적 STW 문제를 해결하기 위해 노력한다. 동시 수집기를 써서 애플리케이션 스레드의 실행 도중 수집에 필요한 작업 일부를 수행해서 중단 시간을 줄인다. 이를 위해서 필요한 두가지 개념을 소개한다.
위의 그림을 보면 GC thread인 T1에서 Java thread인 T2와 T3를 멈춰야 하는 상황이다. T1 에서 T2, T3 에게 멈추라는 명령을 보낸다.
T2는 suspend_enable() 상태였기 때문에 safe_region을 true로 설정하고 스레드를 멈춘다. 그리고 T3가 멈추기를 기다린다.
T3는 suspend_enable() 상태가 아니였기 때문에 T1에게 요청을 받았을 때 바로 멈추지 못했지만 safe_point()를 만나서 멈춘다.
이렇게 모든 스레드가 멈추면 garbage collection이 일어나는 것을 볼 수 있다. 위의 예시에서는 GC thread가 Java thread에게 멈추라는 명령을 보냈지만 safe point를 전역 변수로 두고 애플리케이션 스레드들이 폴링을 통해서 safe point를 보고 스레드를 멈추기도 한다.
1. 처음에 모든 객체는 흰색으로 표시, gc 루트는 회색으로 표시한다.
2. 마킹 스레드가 회색 노드로 랜덤하게 이동하며 거쳐간 노드를 검은색으로 표시하고 해당 노드가 가리키는 노드를 모두 회색 표시한다.
3. 회색 노드가 더 이상 없을 때까지 2번 과정을 되풀이한다.
4. 회색 노드가 없어지면 검은색 노드는 살아남고 흰색 노드는 수집 대상이 된다.
이 과정에서 SATB (snapshot at the beginning)라는 기법이 적극 활용된다. 그렇기 때문에 마킹 스레드가 동작하는 중간에 노드의 정보가 수정되어 라이브 객체가 수정될 수 있다는 단점도 존재한다. 이를 위해서 새로 생긴 객체에 대해서 따로 처리하는 쓰기 배리어같은 기법이 활용되기도 한다.
위와 같은 기법들이 적용된 대표적인 GC인 CMS와 G1 수집기를 살펴보자!
CMS 에서 메모리를 바라보는 관점은 아래와 같다.
오라클 홈페이지에서 가져온 CMS 과정에 대한 설명이다. 애플리케이션 스레드와 동시에 일어나면서 STW이 덜 일어나긴 하지만 Initial Mark, Remark 과정에서는 STW 가 일어나는 걸 확인할 수 있다.
G1 에서 메모리를 바라보는 관점은 아래와 같다. 위에서 봤던 CMS 에서의 관점과 사뭇 다른 것을 확인할 수 있다.
마찬가지로 오라클 홈체이지에서 가져온 G1 과정에 대한 설명이다.
JVM은 인터프리터로 자바 바이트코드를 실행하고 있고 인터프리터로 해석하여 구동하는 환경은 기계어를 직접 실행하는 프로그래밍 환경인 AOT보다 성능이 떨어지기 마련이다. 그렇기 때문에 자바는 동적 컴파일 기능인 JIT를 이용해 이 문제를 해결한다.
JIT 컴파일에서는 우리가 모르는 사이 다양한 기법을 활용해서 우리 코드를 최적화하고 있다. 기법들을 확인해보자!
인라이닝 (Inlining)
루프 펼치기 (Loop Unrolling)
탈출 분석 (Escape Analysis)
단형성 디스패치 (Monomorphic Dispatch)
인트린직 (Intrinsic)
온-스택 치환 (On-Stack Replacement)
세이프포인트 복습 (Safepoint Revisit)
코어 라이브러리 메서드 (Core Library Methods)
GC와 JIT에 관해서 예전에 봤던 글이 새록새록 떠오르네요
그림까지 포함해서 잘 정리하신글 편하게 읽고 갑니다.
아쉽지만 여기도 옥의티가 하나 있네요
"스레드의 제도권을" --> "스레드의 제어권을"