가비지 컬렉션(Garbage Collection, GC)
C/C++과 달리 자바는 개발자가 명시적으로 객체를 해체할 필요가 없다. 이는 자바의 장점 중 하나이다. 사용하지 않는 객체를 메모리에서 삭제하는 작업을 GC라고 부른다.
가비지 컬렉션은 동적으로 할당된 메모리 영역 중 사용하지 않는 영역(쓰레기)를 찾아 지우는 역할을 한다.
GC에 대해 자세히 알기 전에 'stop-the-world'를 먼저 살펴보자.
stop-the-world?
GC를 실행하기 위해 JVM은 애플리케이션 실행을 멈추게 하는데 이를 'stop-the-world(STW)'라고 한다.
모든 GC는 STW를 발생시키는데 STW가 발생하면 GC를 실행하는 Thread를 제외하고 모든 Thread는 작업을 멈춘다. GC 작업이 완료되어야 중단되었던 작업들이 다시 실행이 된다.
GC 알고리즘
Reference Counting
Reference counting 방식은 객체를 참조할 수 있는 방법의 대한 갯수인 reference count를 저장하고, 객체에 접근할 수 있는 방식이 없을 때, reference count가 0이 되면 GC의 대상이 된다.
해당 알고리즘 방식은 순환참조의 문제가 발생할 수 있다는 단점이 있다.
Mark And Sweep
Mark and Sweep은 Root Space에서 부터 시작하여 참조할 수 있는 객체들을 찾으면서 Mark 한다. Mark가 끝나면 힙 내부를 돌면서 Mark 되지 않은 객체들을 지우는 방식이다.
root로부터 연결된 객체를 Reachable, 연결되지 않은 객체를 Unreachable이라고 한다.
위의 그림에서 객체들이 제거된 이후에 분산되었던 메모리가 정리된 것을 확인할 수 있는데 이를 메모리 파편화를 막는 Compaction이라고 한다.
자바는 Mark And Sweep 방식으로 CG를 진행한다.
Heap 영역 구조
위의 그림은 Heap의 영역 구성인데 Heap은 크게 2개의 영역으로 나뉘어진것을 확인할 수 있다.
Young generation과 Old generation 2가지 영역으로 나뉘어져 있다.
Young generation 영역
새롭게 생성한 객체의 대부분이 여기에 위치하게 된다.
대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라진다.
Young 영역에서 객체가 사라질때 (GC가 실행될 때) Minor GC가 발생한다고 한다.
Young generation은 Eden, Survival 0, Survival 1영역으로 나뉜다.
- Eden
- 새롭게 생성된 객체들이 할당되는 영역이다.
- Servival 0, 1
- Minor GC로부터 살아남은 객체들이 존재하는 영역이고, 0 또는 1 둘중 하나는 반드시 비어있어야 한다.
Young generation영역의 동작 과정
- Eden 영역에서 GC가 한 번 발생한 후 살아남은 객체는 Survivor 1과 2 영역 중 하나로 이동된다.
- Eden 영역에서 GC가 발생하면 이미 살아남은 객체가 존재하는 Survivor 영역으로 객체가 계속 쌓인다.
- 하나의 Survivor 영역이 가득 차게 되면 그 중에서 살아남은 객체를 다른 Survivor 영역으로 이동한다. 그리고 가득 찬 Survivor 영역은 아무 데이터도 없는 상태로 된다.
- 이 과정을 반복하다가 계속해서 살아남아 있는 객체는 Old 영역으로 이동하게 된다.
Old generation 영역
접근 불가능 상태로 되지 않아 Young 영역에서 살아남은 객체가 여기로 복사된다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다. 이 영역에서 객체가 사라질 때 Major GC가 발생한다.
young 영역과 old 영역이 모두 꽉차면 Full GC(Minor GC + Magor GC)가 발생한다.
GC 종류
1. Serial GC
Serial GC는 하나의 Thread로 GC를 실행시키는 방식으로 Heap 영역이 작을때 사용한다.
싱글 쓰레드로 GC를 실행시키다 보니 속도가 느리다는 단점이 있다.
2. Parallel GC
Parallel GC(Throughput GC)는 하나의 쓰레드만 사용하는 Serial GC와 다르게 GC를 처리하는 쓰레드가 여러개이다. 그렇기 떄문에 Serial GC보다 STW시간이 짧아지고, 빠르게 객체를 처리한다.
멀티 쓰레드를 사용하기 때문에 메모리가 충분하고 코어가 많을때 사용하는것이 적합하다.
3. CMS(Concurrent Mark Sweep) GC
CMS GC는 STW로 인해 애플리케이션이 멈추는 현상을 줄이고자 만든 GC이다. 위 사진을 보면 총 4가지 단계로 나뉘어서 진행된다.
1. Initial Mark: 클래스 로더에서 가장 가까운 객체 중 살아있는 객체만 찾는다. 이때 stop-the-world가 잠깐 발생한다
2. Concurrent Mark: 방금 찾은 객체에서 참조하고 있는 객체를 따라가며, 지속적으로 마킹한다. (stop-the-world 없이 이루어지기 때문에 다른 스레드가 실행중인 상태에서 동시에 이루어진다.)
3. Remark: concurrent mark 과정에서 변경된 사항(새로 추가되거나 끊긴 객체)이 없는지 다시 한번 마킹하며 확정하는 과정. (stop-the-world 발생한다.)
4. Concurrent Sweep: 마지막으로 쓰레기들을 제거하는 작업을 한다. (stop-the-world 없이 이루어짐)
CMS GC방식은 위와 같이 stop-the-world가 최대한 덜 발생하도록 하여, Java Application이 멈추는 현상을 줄이지만 다른 GC방식보다 메모리와 CPU를 더 많이 사용한다는 단점이 존재한다.
4. G1(Garbage First) GC
G1 GC방식은 Heap을 Region이라는 일정하게 나눠서 메모리를 관리한다. 위 그림을 보면 각각의 영역마다 Young Generation또는 Old Generation으로 활용이 되는것을 볼 수 있다.
java 9이상의 버전부터 기본 GC로 활용되고 있다.
'Java' 카테고리의 다른 글
[Java] Checked Exception과 Unchecked Exception (2) | 2023.02.27 |
---|---|
[Java] Call By Reference와 Call By Value (0) | 2023.01.09 |
JVM(Java Virtual Machine)이란? (0) | 2023.01.01 |
배열 (0) | 2022.05.22 |
반복문 - for문 (0) | 2022.05.22 |