Garbage Collection은 heap 메모리 영역에서 발생하며, 어플리케이션에서 더이상 참조되지 않고 있는 객체를 알아서 해제해주는 작업을 말한다. Garbage Collection이 일어나는 방식에 대해서는 이전 글들을 참조하고, 이번에는 Garbage Collection 알고리즘에 대해 알아보도록 한다.
싱글 스레드로 동작하며 앞서 학습한 가장 기본적인 동작 (Mark - Sweep - Compact)으로 GC가 진행된다. 싱글 스레드로 동작하여 GC가 수행되는 동안 다른 스레드들은 작업을 모두 멈추는 stop the world가 발생한다. 단일 스레드가 GC를 수행하고 있으므로 다른 GC 알고리즘에 비해 시간이 많이 소요된다. 거의 사용되지 않는 GC 알고리즘이다.
그림을 통해서도 알 수 있듯, stop the world의 시간이 짧아졌다. Young 영역에서 일어나는 GC를 단일 스레드가 아닌 멀티 스레드로 병렬처리하고 있다. 주어진 일을 여럿이서 함께 해내면 혼자 할 때보다 작업 시간이 단축되는 것과 같은 원리이다. mark - sweep- compact 방식으로 동작하며 Java 7, 8의 기본 GC 알고리즘이다.
2번의 Parallel GC에서는 Young 영역에서만 GC가 일어났지만 Parallel Old GC에서는 Old 영역까지도 멀티스레드 방식을 사용한다. 이때, Old 영역에서는 mark - summary - compact 알고리즘이 사용된다.
mark 단계에서는 old 영역을 region별로 나누고 region별 자주 참조되는 객체들을 식별(mark)한다. 이후 summary 단계에서는 region별 통계정보로 살아남은 객체들의 밀도가 높은 부분이 어디까지인지 dense prefix를 정한다. 오랜 기간 참조된 객체는 앞으로 사용할 확률이 높다는 가정 하에 dense prefix를 기준으로 compact 영역을 줄인다. compact 단계에서는 compact 영역을 destination과 source로 나누어, 살아남은 객체는 destination으로 이동시키고 참조되지 않는 객체는 제거한다.
stop the world로 어플리케이션이 멈추는 현상을 줄이고자 나온 GC 알고리즘이다. 자주 참조되는 객체를 한번에 찾지 않고 4번에 걸쳐 찾는 방식을 사용한다. 해당 GC는 stop the world 시간을 최소화하는 데 그 목적이 있다. low-latency GC로도 알려져 있다. Young 영역에 대한 GC는 Parallel GC와 동일하다.
CMS GC는 stop the world 속도가 짧으므로 응답 속도가 중요한 어플리케이션의 경우 사용된다. stop the world 시간은 짧으나, 다른 GC 방식들보다 CPU와 메모리 소모가 많고 compaction 단계가 기본적으로 제공되지 않는다. 조각난 메모리가 많을 경우 compaction 작업을 실행하면 다른 GC보다 stop the world 시간이 더 길어진다. Java 9에서 deprecated 되었고 Java 14에서 사용이 중지되었다.
Java 9의 기본 GC 알고리즘이다. 현재 GC 알고리즘들 중 stop the world 시간이 가장 짧다. 이전 알고리즘들과는 달리 CMS GC는 heap 메모리를 region이라는 일정한 부분으로 나누어 메모리를 관리한다. heap을 균등하게 region으로 나누고 각 region을 논리적으로 구분(Eden region인지, Survivor region인지, Old region인지)하여 객체를 할당한다.
G1 GC에서는 Eden, Survivor, Old region에 더하여 Humongous와 Available/Unused라는 2가지 region을 추가하였다. Humongous는 region 크기의 50%를 초과하는 객체를 저장하는 region을 의미하고, Available/Unused는 사용되지 않은 region을 의미한다.
G1 GC의 핵심은 이전 알고리즘들과는 달리 heap 전체에 대한 탐색을 진행하지 않고 부분적으로(region 단위) 탐색하여 garbage가 많은 region에 우선적으로 GC를 수행한다. compaction 과정에서도 heap 메모리 전체에 대해 compaction을 진행하지 않고 region 내에서 compaction이 이루어지므로 전체적으로 GC에 소요되는 시간이 감소한다.
G1 GC에서 Minor GC와 Major GC가 동작하는 방식은 다음과 같다.
특정 region에 객체를 할당하다가 해당 region이 꽉 차면 다른 region에 객체를 할당하고 꽉찬 region에 대해 Minor GC가 수행된다. 이때, G1 GC는 각 region을 추적하고 있으므로 garbage가 많은 지역을 찾아 Mark and Sweep 을 진행한다.
Eden region에서 GC가 수행되면 살아남은 객체를 mark 하고 메모리를 회수(sweep) 한다. 그리고 살아남은 객체들을 다른 region으로 이동시킨다. 이동된 region이 Available/Unused 지역이면 해당 지역은 Survivor region이 되고, Eden region은 Available/Unused 지역이 된다.
Minor GC 발생 시
- Eden -> Available/Unused
- Available/Unused -> Survivor
시스템이 운영되다가 객체가 너무 많아 빠르게 sweep이 불가능할 때 Major GC(Full GC)가 수행된다. 기존의 GC 알고리즘은 모든 Heap 영역에서 수행되어 시간이 오래 소요되었다. 하지만 G1 GC는 어느 영역에 garbage가 많은지 알고 있으므로 GC를 수행할 지역을 조합하여 해당 region에 대해서만 GC를 수행한다. 이러한 작업은 Concurrent하게 발생하여 애플리케이션의 지연도 최소화 가능하다.
G1 GC가 다른 GC 알고리즘들에 비해 자주 호출될 것이나, 작은 규모의 메모리 정리 작업이고 concurrent하게 수행 가능하므로 어플리케이션이 크게 지연되지 않는다. 또한 garbage가 많은 region에 대해서 정리를 하므로 훨씬 효율적이다.
[참조] https://dar0m.tistory.com/261
[참조] https://jgrammer.tistory.com/143
[참조] https://memostack.tistory.com/229
[참조] https://reference-m1.tistory.com/113