Java Garbage Collector
Garbage Collector란?
더 이상 사용하지 않는 객체를 자동으로 식별해 제거해 개발자가 의도적으로 메모리를 해제할 필요 없게 해주는 도구이다. GC를 통해 사용 가능한 메모리 영역이 없어 OutOfMemoryError
가 발생하면 WAS가 다운될 수 있다.
GC는 총 3가지의 단계로 이루어진다.
- Mark: 사용 중인 객체와 사용하지 않는 객체를 식별한다.
- Sweep: 사용되지 않는 객체를 제거한다.
- Compact: 남아 있는 객체를 모아 메모리 단편화를 방지한다.
많은 사람들이 Java에만 존재한다고 생각하지만, 실제론 자바뿐만 아닌 파이썬, 자바스크립트, Go 등에도 기본적으로 내장이 되어있다.
하지만 이 글에선 Java의 가비지컬렉션이 어떻게 이루어지는지 정리해보자.
단점은 없을까?
-
메모리가 해제되는 시점을 알기 어렵다.
-
GC가 동작하는 동안 다른 작업을 멈추기 때문에
Stop-The-World
문제가 발생할 수 있다.Stop the world란?
GC를 하기 위해 JVM이 다른 프로그램 실행을 멈추는 행위를 의미함.
GC와 관련된 Thread외의 모든 Thread는 멈춘다. 따라서, 해당 멈추는 시간을 최소화 하기 위한
GC 튜닝
이 필요하다.
Java GC
JVM의 기본적인 메모리 영역은 크게 클래스 영역
, 자바 스택
, 힙
, 네이티브 메소드 스택
4가지로 구분되는데 그 중 동적 메모리 영역인 Heap 영역의 메모리에 GC가 작동한다.
GC는 2가지 전제조건을 기반으로 가설을 잡았다.
- 대부분 객체는 금방 접근 불가능 상태가 된다.
- 오래된 개체가 최근에 생성된 객체를 참조하는 것은 아주 적다.
이 전제에 대한 가설을 weak generational hypothesis
라고 한다.
이를 위해 GC는 영역을 2가지로 구분한다.
Young Generation
새롭게 생성된 객체가 Young 영역에 존재한다. Young 영역은 총 3가지 영역으로 구분되는데 하나의 Eden영역과 2개의 Survivor 영역이다.
새롭게 생성된 객체는 Eden 영역에 존재하며, GC가 발생 후 살아남으면 Survivor 영역으로 이동하는 구조이다.
이 영역에서 발생하는 GC를 Minor GC라고 한다.
Old Generation
Young 영역에서 살아남은 객체들은 Old Generation에 존재한다.
대부분 오래 살아남은 객체이기에 Young보다 큰 영역을 할당받기 때문에 GC가 상대적으로 적게 일어난다.
이 영역에 512Byte의 카드 테이블이 존재하는데, Old 영역의 객체가 Young 영역의 객체를 참조하는 정보를 표시하고 참조를 추적한다. Young영역의 Minor GC할땐, 카드테이블을 통해 참조한다.
여기서 일어나는 GC는 Major GC라고 한다.
Old 영역에서 GC가 일어난 후 생존한 객체는 Permanent Generation로 이동한다.
(단, 이 영역에서도 Major GC가 발생한다.)
Serial GC
Old 영역의 GC알고리즘이다. Mark
→ Sweep
→ Compact
- Old 영역에서 살아있는 객체를 Mark하다.
- heap의 앞부분부터 확인하여 살아 있는 것만 남긴다.(Sweep).
- 남아있는 객체가 연속되게 쌓이도록 힙의 앞부분부터 채워서 객체가 존재하는 부분과 없는 부분을 구분한다. (Compact)
G1 알고리즘
Java9이상에선 G1 알고리즘을 기본 값으로 한다.
바둑판 형식으로 Heap을 여러 영역으로 나누고, 각 영역에 대해 독립적으로 해당 영역이 가득차면 다른 영역에 객체를 할당하고 가비지 컬렉션을 한다.
이 방식은 Young →
작동 방식: 힙을 여러 개의 영역(Region)으로 나누고, 각 영역을 독립적으로 가비지 컬렉션합니다. 필요에 따라 Young 영역과 Old 영역을 동시에 수집할 수 있습니다.