운영체제 Heap 메모리 영역에 접근해 사용하지 않는 Object를 정리해주는 JVM의 구성요소입니다. 개발자가 직접적으로 메모리 해제를 C/C++과 달리, JVM에서는 GC를 제공해 개발에만 집중하고, Memory Leak을 신경쓰지 않아도 됩니다.
JVM은 다음과 같은 구성요소가 존재합니다.
대부분의 객체는 생성되고 얼마되지 않아 Unreachable Object가 됩니다. JVM에서 Heap에 Object를 적재할 때, 생성시기와 생존기간을 고려해 크게 Young Generation, Old Generation으로 구역을 나누어, GC를 위한 데이터 접근을 효율적으로 수행합니다.
Unreachable Object: 메서드가 종료되거나 참조가 사라져 사용할 수 없는 객체.
또한, JVM을 설계할 때, 객체는 금방 Unreachable 상태가 된다는 것을 가정하고 시작하므로 Young Geneartion 영역이 Old Generation 영역보다 작고, GC도 더 자주 수행됩니다.
YG,OG영역에서 수행되는 GC를 각각 Minor GC, Major GC 라고 하며 수행방법또한 차이가 있지만, 모두 Stop-the-world, Mark&Sweep동작이 기본이 됩니다.
Stop the world동작은 GC를 수행하는 스레드를 제외한 모든 스레드를 중지하면서 GC과정을 Thread-safe하게 수행하도록 하는 과정입니다.
Mark & Sweep동작은, 전역변수와 현재 scope의 로컬 변수를 루트로 시작해 참조하고 있는 Object를 탐색하며 mark bit를 true로 변경해줍니다.
이후 Mark되지 않은 Object(회색부분)를 삭제해주며, GC과정을 수행합니다.
이 때, 메모리에서 데이터가 부분적으로 존재하는 파편화 문제가 발생하며, Compaction 알고리즘을 통해 이를 해결합니다.
Young Generation(YG)영역의 경우, 처음 생성한 객체를 저장하는 Eden영역과, Eden영역에서 살아남은 객체가 저장되는 Survival영역이 두 개 존재합니다. 동적으로 할당된 메모리 영역(Heap)에서 Unreachable Object를 찾아 삭제하는 역할 수행합니다.
Survival0,1 두 개의 영역은 서로의 역할을 번갈아가며 3번과 4번 과정이 여러번 반복됩니다. 이때, Eden영역에서 살아남은 Object는 Age값이 초기값이지만, Survival0(또는 1) 영역의 객체들은 살아남은 기간만큼의 Age값을 갖습니다.
Old Generaion(OG)영역의 경우, YG영역에서 특정기간 이상 살아남은 객체들이 존재하는 영역입니다. 데이터가 가득 차면 GC를 수행하므로 그만큼 GC실행빈도는 적고, Stop the wolrd(STW)시간은 약 10배 이상 깁니다.
GC의 STW시간은 애플리케이션 실행 성능에 큰 영향을 미칩니다. STW를 줄이는것이 GC의 역사라고 해도 과언이 아닐 정도로, GC에서 중요한 지표로 사용됩니다.
여러가지 GC 알고리즘이 있지만, JDK8 이하 버전에서 사용된 SerialGC, ParallelGC JDK9이상 버전에서 사용중인 G1GC, JDK15에서 Production버전으로 준비되어 좋은 성능을 내고있는 ZGC에 대해서 Reference탭에 있는 링크를 통해 확인해주세요.
YG영역에서는 앞부분 에서 설명한 GC를 그대로 수행하지만, OG 영역에서는 mark&sweep 이후 각 객체들이 연속되게 쌓이도록 Compaction 과정을 수행합니다. JDK 8버전 이하에서, 클라이언트 애플리케이션을 위한 GC 기본값으로 사용했습니다.
SerialGC와 기본 알고리즘은 같지만, GC를 처리하는 스레드의 수가 여러개라는 차이가 있습니다. JDK8버전 이하에서, 서버 애플리케이션을 위한 GC 기본값으로 사용됩니다.
Naver D2, Java Garbage Collection
[10분 테코톡] 👌던의 JVM의 Garbage Collector