Java에서 GC는 Heap메모리에 할당됐던 객체들 중에서 더 이상 사용되지 않는 객체들을 메모리에서 제거해주는 기능을 이야기한다.
C언어에서는 메모리에서 데이터를 해제해주는 것을 직접 구현해줘야하지만, Java를 포함해서 최근에 나온 언어들은 대부분 GC를 통해서 자동적으로 메모리 관리를 해준다.
오래된 데이터를 메모리에서 제거하는 Full GC가 실행되면, GC가 실행되는 쓰레드 외의 쓰레드들은 모두 일시중지하게 된다. 그래서 잠깐 동안 서비스가 멈추게 되는데, 이를 "Stop the world"라고 일컫는다.
GC의 성능은 이 "Stop the world"를 얼만큼 짧게 만드는지가 관건이라고 할 수 있다.
Heap 메모리 영역은 아래와 같은 구조를 갖고 있다.
처음 생성된 객체들이 존재하는 Eden, 한번 이상의 GC에서 살아남은 객체들이 존재하는 Survivor0, Survivor1이 Young generation이라고 불리는 공간이다.
Young generation에서 이루어지는 GC를 Minor GC라고 부르고, Eden영역이 꽉 차면 Minor GC는 실행된다.
GC에서 살아남으면, age
값이 1씩 올라가는데, age
가 임계치를 넘어가면, Old generation으로 이동하게 된다.
Young generation에서도 GC되지 않은 오래된 객체들이 존재하는 영역이 Old generation이고, 여기서 이루어지는 GC는 Major GC 또는 Full GC라고 부르고, Old generation영역이 꽉 차면, Major GC는 실행된다.
GC의 알고리즘은 다양하지만, 가장 기본적이면서 대표적으로 Mark, Sweep이라고 일컫어지는 과정을 통해서 GC는 동작한다.
먼저, Mark는 GC는 Heap메모리에 할당된 데이터가 실제로 사용되고 있는 객체인지에 대한 여부를 판단하는 과정이다. 사용되고 있는 데이터라면 Reacheable
, 사용되고 있지 않는 객체라면 Unreacheable
로 구분한다.
Mark
과정에서 감지한 사용되지 않는(Unreacheable) 객체들을 메모리에서 해제하는데, 이 과정을 Sweep이라고 부른다.
이 때, GC에서 살아남은 횟수에 따라서 위에서 이야기한 Heap메모리 내의 영역을 이동하게 되는 것이다.
Mark라는 과정을 통해서 사용되고 있는 객체인지에 대한 여부를 판단한다고 했는데, 어떤 것을 보고 이 객체가 사용중인지 아닌지 알 수 있을까?
GC는 Root set이라고 불리는 3개의 메모리 영역(Stack, JNI, Method area)들을 기점으로 스캔을 한다.
각각은 Heap에 할당된 객체들을 참조하고 있어서 이 영역들에 존재하는 모든 데이터를 스캔하면, Heap에서 참조되고 있지 않는 객체들을 판별할 수 있는 것이다.
이렇게 참조되고 있는 Reachable데이터와 그렇지 않은 Unreachable데이터를 구분하는 Mark
작업이 이루어진다.
GC는 그 동작방식 또는 알고리즘에 따라서 다양한 종류가 존재한다. 각각을 한번 알아보자.
java -XX:+UseSerialGC -jar Application.java
Mark & Sweep
, Major GC에서는 Mark & Sweep & Compact
알고리즘을 사용한다.java -XX:+UseParallelGC -jar Application.java
java -XX:+UseG1GC -jar Application.java
📌 오라클 공식문서
java -XX:+UseZGC Application.java