가비지 컬렉션(GC)이란?
가비지 컬렉션(GC)란 자바의 메모리 관리 방법이다. JVM 상의 Heap 영역에서 동적으로 할당했던 메모리 중 더 이상 사용하지 않는 메모리 객체(가비지)를 모아 주기적으로 제거하는 프로세스이다.
기본적으로 Heap 영역의 메모리는 객체 생성 시에 할당되므로, 더 이상 참조되지 않는 경우에 가비지로 판단되어 제거된다. 이러한 가비지가 제거되지 않는다면, 메모리가 해제되지 않아 사용할 수 있는 메모리가 점점 줄어드는 메모리 누수(Memory Leak)이 발생하게 되므로 중요하다.
Stop -The-World란?
Stop-The-World란, GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것이다.
GC를 수행하는 동안, GC 관련 쓰레드를 제외한 모든 쓰레드는 정지되므로 시스템 성능에 문제가 될 소지가 있다.
같은 맥락에서 자바에서 메모리를 명시적으로 지정하여 해제하기 위해서, System.gc() 메서드를 호출하는 것은 극히 지양해야 한다.
Mark and Sweep이란?
- Mark : 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업
- Sweep : Mark 단계에서 사용되지 않는다고 판단되는 메모리를 해제하는 작업
- Compact : 파편화된 메모리 영역을 앞에서부터 채워나가는 작업(Compaction)
Stop The World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수와 Reachable 객체를 스캔하면서 각각 어떤 객체를 참조하고 있는지 탐색한다. 이후 사용되고 있는 메모리를 식별해서 Mark한다.
이후 Mark가 되지 않는 객체들을 메모리에서 제거하는 과정이 Sweep이다.
이어 분산된 객체들을 Heap의 시작 주소로 모은 뒤, 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다.
단, Compact 과정은 GC 알고리즘에 따라 하지 않는 경우도 있다.
Minor GC & Major GC
가비지 컬렉터는 Weak Generational Hypothesis라는 두 가지 전제 조건 하에서 설계되었다.
- 대부분의 객체는 금방 접근 불가능 상태(Unreachable)가 된다.(객체에 유효한 레퍼런스가 없는 상태)
- 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
또한 이러한 가설의 장점을 최대한 살리기 위해서, JVM의 Heap 영역을 객체의 생존 기간에 따라 두 가지로 나누게 되었다. 이는 각각 Young 영역과 Old 영역이다.
Young Generation
- 새롭게 생성된 객체가 저장된다.
- 대부분의 객체는 금방 접근 불가능 상태가 되므로, Young 영역에서 생성되었다가 사라진다.
- 이를 Minor GC라고 한다.
Old Generation
- Young 영역에서 접근 가능 상태가 계속 유지되어 살아남은 객체가 복사되는 영역
- Young 영역보다 훨씬 크게 할당되므로, Young 영역보다 GC가 적은 빈도로 발생한다.
- 이를 Major GC 혹은 Full GC라고 한다.
Permenent Generation은
- class loader에 의해 로드되는 Class, Method에 대한 메타 정보가 저장되는 영역
- Java 8 이후로 Native Method Stack에 편입되게 되었다.
Minor GC의 과정
Minor GC의 경우, 앞서 설명한 Young 영역에서 발생하는 GC이다.
Young 영역은 크게 3가지 영역으로 구성된다. 이를 통해 객체의 생존 기간을 더욱 세밀하게 제어할 수 있다.
Eden 영역
- new를 통해 새롭게 생성된 객체가 저장되는 영역이다.
- Eden 영역이 꽉 차면 Minor GC(mark and sweep)가 발생된다.
- 여기서 살아남은 객체는 아래 Survivor 영역으로 이동된다.
Survivor 영역(0, 1)
- 최소 1번 이상의 GC 이후 살아남은 객체가 존재하는 영역이다.
- 해당 영역의 경우, 2개의 영역이지만 반드시 1개의 영역에만 데이터가 존재해야 한다.
세부적인 프로세스를 살펴보면 아래와 같다.
1. 새롭게 생성된 객체의 경우, Eden 영역에 할당된다.
2. Eden 영역이 가득차게 된 경우, Minor GC가 발생되어 S0 영역으로 이동되며, Unreferenced 객체들은 Sweep 되어 메모리가 해제된다. 해당 과정에서 살아남은 객체의 경우 Generation 값이 1 증가한다.
3. 이후, 같은 프로세스에 따라 사용중인 객체가 S1으로 이동한다. 이때 S0에 존재하던 객체는 Generation이 1 증가한다.
이동 이후 S0과 Eden에 남아 있는 Unreferenced 객체들은 모두 삭제된다.
4. 이어지는 Minor GC에서는 다시 S0을 기점으로 살아남은 모든 객체가 이동되기를 반복한다.
이후 계속 살아남은 객체는 Old 영역으로 이동(Promotion)된다.
Major GC의 과정
Major GC(Full GC)는 객체들이 계속 Promotion 되어 Old 영역의 메모리가 부족해지면 발생한다.
기본적으로 mark-and-sweep의 방식으로 동작한다.
하지만 Old Generation의 경우 Young Generation에 비해 큰 공간을 갖게 되므로, GC 수행 시간이 Minor GC에 비해 훨씬 오래 걸리게 된다. 일반적으로 Minor GC의 경우 0.5~1초 사이의 시간이 소요되지만 Major GC의 경우 Minor GC 대비 10배 이상의 시간이 소요된다.
따라서 이러한 Stop-The-World 시간을 최적화하기 위해 다양한 GC 알고리즘이 개발되어 왔다.
다음 포스팅에서는 여러 GC 알고리즘을 소개하고 서로 비교하는 포스팅을 작성하고자 한다.
References
- https://d2.naver.com/helloworld/1329
- https://mangkyu.tistory.com/118
- https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
'CS > Java' 카테고리의 다른 글
Java의 Collections Deep Dive - 1 : HashMap이란? (0) | 2024.05.17 |
---|---|
@Override가 뭐야? (0) | 2023.03.29 |
[자바] 인터페이스는 객체지향의 4대 특성중에 무엇을 지니는가? (1) | 2023.03.28 |
[자바] 객체지향의 4가지 특성 (0) | 2023.03.28 |