[JAVA] Garbage Collection(GC)
GC란?
C, C++ 언어와 달리 자바는 힙 영역에 할당한 메모리 중 더이상 사용되지 않는 객체를 자동으로 해제합니다. 이 과정을 Garbage Collection(GC) 라고 하며 가비지 컬렉션은 주기적으로 일어납니다. JVM이 자동으로 메모리를 해제해 주기 때문에 개발자들은 메모리 누수를 걱정할 필요없이 개발에 집중할 수 있다는 장점이 있습니다. 예를 들어 다음과 같은 코드가 있다고 생각해 보겠습니다.
int[] arr = new int[50];
arr = null;
//do something...
arr 배열은 Heap 영역에 저장됩니다. 하지만 곧바로 arr = null 명령어에 의해서 힙 영역에 할당했던 int[50] 배열은 더이상 참조될 수 없게 됩니다. 이런 일이 지속적으로 일어날 경우 힙 영역은 참조되지 않는 객체들로 차게 될 것이고 저희가 사용할 수 있는 메모리 자원은 계속 줄어들 것입니다. JVM은 GC과정을 통해 이렇게 사용되지 않는 객체들을 해제시켜 줍니다.
GC는 어떻게 사용되지 않는 객체를 알아내나요?
위 그림은 JVM의 Runtime Data Area를 나타냅니다. Heap 영역을 참조하는 방법은 네 가지가 있습니다.
- 메서드 영역에서 객체를 참조
- 지역변수에서 객체를 참조(Java Stack)
- JNI에 의해 생성된 객체에 대한 참조(Native Stack)
- 힙 영역 내의 객체가 다른 객체를 참조
4번을 제외한 세 가지 영역을 Root set이라고 하고, root set에서 뻗어나가는 참조 중 유효한 참조가 아닌 경우 그 객체는 unreachable이며 GC의 대상이 됩니다.
Heap 영역 구조
GC의 과정을 살펴보기 전에 힙 영역의 구조를 살펴보겠습니다.
힙 영역은 Eden, Survival 0, Survival 1로 구성된 Young Generation, Old generation 두가지 영역으로 구성됩니다. Minor GC는 Young Generation에서 일어난 GC, Major GC는 Old Generation에서 일어난 GC를 말합니다.
GC의 동작과정
- 처음 할당된 객체들은 Eden 영역에 저장됩니다.
- Eden 영역이 차게 되면 Minor GC가 일어나며 reachable 객체들은 Survival 0 영역에 저장됩니다.
- 또다시 Eden 영역이 차게 되면 Minor GC가 일어나며 Survival 0 에서 살아남은 객체들과 함께 Survival 1 영역에 저장됩니다. 여기서 두 개의 Survival 영역 중 한 영역은 비어있어야 합니다. Survival 영역에서 살아남은 객체들은 age 값이 1 증가합니다.
- Minor GC가 반복되면 살아남은 객체들의 age 값이 계속 증가하게 되며 이 age 값이 특정 값을 넘어가게 되면 Old Generation 에 저장됩니다. 이 과정을 Promotion 이라고 합니다.
- Old Generation 영역이 가득 차게 되면 Major GC(Full GC)가 일어나고 살아남은 객체는 Old Generation에 저장됩니다. Full GC는 시간이 많이 걸리기 때문에 애플리케이션의 안정성과 성능에 영향을 크게 미칠 수 있습니다.
Mark and Sweep
- Mark : Root Set으로부터 reachable 객체를 체크하는 과정을 Mark 합니다.
- Sweep : Reachable 이 아닌, Unreachable 객체들을 해제하는 과정입니다.
- Compact : 살아남은 객체들을 한쪽으로 모읍니다.
Stop the World
- GC가 진행될 때는 GC를 수행하는 쓰레드 외에 다른 쓰레드들은 정지됩니다. 이 특징을 Stop the World라고 합니다.
- 프로그램이 일시정지 상태가 되기 때문에 이 시간을 줄이는 것이 GC의 쟁점입니다.