<JAVA> GC (가비지 컬렉션)이란?
📚 Heap 의 구성
Young Generation
인스턴스를 처음 생성했을 때 메모리에 배정되는 영역. 이곳에서 일어나는 GC를 Minor GC 라고 함
Eden
- 객체가 최초로 할당 되는 곳
- Eden 이 가득 찬 경우 객체들의 참조 여부를 확인하고, 참조가 없어진 경우 삭제 / 참조가 있는 경우 Survivor 로 넘긴다
Survivor
- Survivor 0 과 Survivor 1 으로 구성
- 살아 있는 object 들은 Survivor 0 이나 Survivor 1 중 하나만 사용함
Old Generation
Minor GC 를 여러번 거치고도 살아남은 인스턴가 배정되는 영역. 이곳에서 일어나는 GC 를 Major GC 라고 함
Old
- Survivor 영역에서 살아남아 오랫동안 참조 되었고 앞으로도 사용될 확률이 높은 Object들을 저장하는 영역
Permanent Generation
Permanent
- Java 8 이후 Metaspace 로 변경
- Permanent 영역은 힙 공간이 아니고 OS 에서 관리함
Minor GC vs Major GC
자바를 처음 만든 사람들은 두가지 가설을 바탕으로 GC 를 설계
-
- Most allocated objects are not referenced for long, that is, they die young.
-
대부분의 객체는 금방 접근 불가능한 상태가 된다. 즉, 금방 garbage 가 된다
-
- Few references from older to younger objects exist.
-
오래된 객체에서 젊은 객체로의 참조는 아주 적게 일어난다
그렇기 때문에 자주 객체가 UnReachable 상태가 되는 Young Reference 영역을 구분해 주기적으로 GC 가 발생하고,
🔩 GC 사용 알고리즘 종류
1. Reference Counting
-
Heap 에 선언된 객체들이 해당 객체에 몇가지 방법으로 접근 할 수 있는지에 대한 숫자를 가지고 있는 Reference count 를 가지고 있고, 이 숫자가 0이 되는 경우 참조하는 변수가 없다고 생각하고 해당 레퍼런스를 삭제
-
하지만 위의 노란색 부분 처럼 레퍼런스가 계속해서 참조되는 순환참조가 발생할 수 있음
2. Mark-Sweep-Compaction
- GC가 사용되지 않는 객체를 메모리에서 제거하는 과정인만큼, GC 대상객체를 식별하고 제거하며 객체가 제거되어 파편화된 메모리 영역을 앞에서부터 채워나가는 작업을 수행
Mark :: 사용되지 않는 객체를 식별하는 작업
- GC 가 Stack 의 모든 변수를 스캔하면서 각각 어떤 객체를 참조 하고 있는지 찾아서 마킹
- Reachable Object 가 참조하고 있는 객체도 찾아서 마킹
Sweep :: 사용되지 않는 객체를 제거하는 작업
- 마킹되지 않은 객체(Unreachable)를 Heap 에서 제거
Compaction :: 파편화된 메모리 영역을 앞에서부터 채워나가는 작업
- Mark & Sweep 방식으로 살아남은 객체를 앞에서부터 채어나가도록 정리
🏋️♀️ GC 가 일어나는 과정
Reachable 객체 vs Unreachable 객체
GC Roots
: 참조하는 가장 상위 값이며, 밑의 내용들이 될 수 있음
- Stack 의 로컬 변수
- Method Area 의 Static 변수
- Native Method Stack 의 JNI 참조
Reachable Object
: GC Roots 에서 참조하는 객체들
Unreachable Object
: GC Roots 에서 참조하지 않는 객체들
과정
- 새로운 객체는 Eden 영역에 할당된다
- Eden 영역이 모두 사용된 경우 GC 발생 (Minor)
- Eden 의 Reachable 객체는 Survival 0 으로 옮겨지고, Unreachable 객체는 Eden 에서 사라진다.
Survival 0 가 가득 찬 경우
- Mark & Sweep 과정이 일어나고 Reachable 객체는 Survival 1 으로 옮겨진다
- 이동한 객체는 Age 값이 증가됨
Survival 0, 1 중 차있는 영역에 계속 쌓이고, survival 0,1 중 하나는 반드시 비어있게 됨
계속 Minor GC 를 반복하다 Age 값이 특정 값 이상을 넘어서면 Old Generation 영역으로 옮겨진다 => promotion
자바 8 의 Parallel GC 방식 사용 기준 15가 되면 promotion 이 진행
⛔️ Stop The World
GC 를 실행하기 위해 JVM 이 어플리케이션 실행을 멈추는 것
➡️ GC 를 실행하는 스레드 외 다른 스레드는 모두 동작을 멈춤
➡️ stop-the-world 시간을 최소화 해야함
🔄 GC 종류와 변화
Serial GC
-
-XX:+UseSerialGC
-
가장 단순한 방식의 GC로 싱글 스레드(스레드 1개)로 동작한다.
-
싱글 스레드로 동작하여 느리고, 그만큼
Stop The World
시간이 다른 GC에 비해 길다. -
Mark & Sweep & Compact
알고리즘을 사용 -
보통 실무에서 사용하는 경우는 없음 (디바이스 성능이 안좋아서 CPU 코어가 1개인 경우에만 사용)
Parallel GC
-
-XX:+UseParallelGC
-
Java 8
의 default GC -
Young 영역의 GC를 멀티 스레드 방식을 사용하기 때문에,
Serial GC
에 비해 상대적으로Stop The World
가 짧다
Parallel Old GC
-
-XX:+UseParallelOldGC / -XX:+ParallelGCThreads=n
-
Parallel GC
는 Young 영역에 대해서만 멀티 스레드 방식을 사용했다면,Parallel Old GC
는Old
영역까지 멀티스레드 방식을 사용 -
-XX:+ParallelGCThreads=n
옵션으로 멀티 스레드 개수를 지정할 수 있음
CMS GC (Concurrent Mark Sweep GC)
Stop The World
로 Java Application이 멈추는 현상을 줄이고자 만든 GC
Reacable
한 객체를 한번에 찾지 않고 나눠서 찾는 방식을 사용 (4 STEP
으로 나눠짐)
Initial Mark
: GC Root가 참조하는 객체만 마킹 (stop-the-world 발생)Concurrent Mark
: 참조하는 객체를 따라가며, 지속적으로 마킹. (stop-the-world 없이 이루어짐)Remark
: concurrent mark 과정에서 변경된 사항이 없는지 다시 한번 마킹하며 확정하는 과정. (stop-the-world 발생)Concurrent Sweep
: 접근할 수 없는 객체를 제거하는 과정 (stop-the-world 없이 이루어짐)
위와 같이 stop-the-world가 최대한 덜 발생하도록 하여, Java Application이 멈추는 현상을 줄임
G1 GC
Java 9+
의 default GC
현재 GC 중 stop-the-world의 시간이 제일 짧음
CMS GC 를 개선하여 만든 GC로 위에서 살펴본 GC와는 다른 구조를 가진다. (아래 그림 참고)
Heap을 Region
이라는 일정한 부분으로 나눠서 메모리를 관리한다.
전체 Heap에 대해서 탐색하지 않고 부분적으로 Region 단위로 탐색하여, 각각의 Region에만 GC가 발생한다.