동적으로 할당된 메모리 영역 중 더이상 사용하지 않는 메모리를 해제하는 것.
GC는 이러한 가설의 전제하에 도입되었기 때문에 생명 주기가 짧은 객체에 대한 빠른 생성과 제거를 보장하는 성격을 가지고 있다.
reachable 객체
Root Set 으로 부터 어떤 식으로든 관계가 있는 객체
unreachable 객체
관계가 없는 객체
Root Set: 객체들 간에 참조 사슬의 시작점. 더 알아보기
Mark
Root Set 으로 부터 모든 변수를 스캔하면서 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
-> reachable, unreachable 객체 식별
Sweep
unreachalbe 객체들을 Heap에서 제거한다.
sweep 단계에서 발생하는 단편화문제를 해결하기위해 분산된 객체들을 Heap의 시작주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 분리한다.
GC알고리즘에 따라 사용할 수도 안할수도 있다.
Young Generation
새로운 객체들이 할당되는 영역으로 생명주기가 짧은 객체가 위치한다. 이 영역에서 발생하는 GC를 Minor GC라고 한다.
Old Generation
Young Generation에서 오랫동안 살아남은 객체들이 이동하여 존재하는 영역으로 생명주기가 긴 객체가 위치한다. 이 영역이 가득차면 발생하는 GC를 Major GC라고 한다.
Metaspace
JDK8 이전의 Perm영역을 대체하는 것으로 클래스나 메소드의 메타정보가 존재하는 영역 (native영역은 Heap에 있지 않으며 OS에 의해 크기가 동적으로 조정된다.)
Survivor 영역(S0, S1) 은 단지 구분하기 위해 0, 1 로 나뉘어져 있으며 우선순위는 없다. 다만, 하나의 영역에 데이터가 존재하면 반드시 다른 영역에는 데이터가 없어야 한다.
새로운 객체가 Eden영역에 할당된다.
Eden영역이 꽉 차면(더이상 새로운 객체를 할당할 수 없을 때) Minor GC가 발생한다. -> Mark and Sweep 수행
reachable객체는 하나의 Survivor영역으로 이동하고 unreachable객체의 메모리는 해제된다.
객체의 생존 횟수를 카운트하기 위해 Minor GC 에서 객체가 살아남은 횟수를 의미하는 age를 object header에 기록한다. 이때 age값이 1증가한다.
Eden영역이 다시 가득차게 되면 Eden과 사용중인 Survivor영역에서 Minor GC가 발생하고 reachable객체는 사용하지 않는 Survivor영역으로 이동한다. 이때도 역시 살아남은 객체는 age값이 1증가한다.
Minor GC가 수행될 때 마다 살아남은 객체는 age값 1증가한다.
객체의 age가 임계점(MaxTenuringThreshold)에 도달하면 Old Generation으로 이동. (이때 이동되는 객체들을 promoted되었다고 한다.)
promted된 객체들로 Old Generation이 가득차면 Major GC발생한다. Old Generation 영역은 보통 Young Generation영역보다 크게 할당된다.
약한 세대 가설에 따라 대부분의 객체는 금방 unreachable상태가 되기 때문에 Minor GC는 자주 발생하고, Minor GC가 수행되었을 때 수거되는 메모리가 많다. 반면, Major GC는 Minor GC보다 더 오래걸리며(STW가 길기 때문) 수거되는 메모리가 적다.
메모리나 cpu core가 부족할 때 사용할 수 있으며, 멀티 프로세스 환경에서도 소형 데이터셋(최대 100MB 정도)를 다루는 애플리케이션이면 쓸만하다.
Inital Mark
Root Set에서 참조상 가까운 객체만 1차적으로 찾아가며 unreachable객체를 판단하고 종료. 탐색깊이가 얕으므로 STW가 짧다.
Concurrent Mask
Inital Mark단계에서 GC대상으로 식별된 객체들을 따라가며 참조가 끊긴 객체 확인. 다른 스레드가 실행 중인 상태에서 동시에 진행(STW 없음)
Remaak
Concurrent Mark 단계의 결과를 검증. 이전 단계에서 식별한 객체를 다시 추적하여 추가로 확인되거나 참조가 제거되었는지 확정한다. Remark단계는 STW를 유발하기 때문에 STW 시간을 최대한 줄이기 위해 멀티스레드로 수행된다.
Concurrent Sweep
unreachable객체 정리, 다른 스레드가 실행되고 있는 상황에서 진행(STW 없음)
G1 GC는 보완이 필요합니다! 참고만해주세요.
자바 Heap의 메모리를 회수할 때 최대한 살아있는 객체가 적게 들어있는 Region을 수집한다. 가장 살아있는 객체가 적을수록 garbage란 의미이고, 따라서 이름도 쓰레기 우선(Garbage First) 수집 이란 이름이 붙게 되었다.
G1 GC에는 추가되는 영역이 있다.
Humongous : Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간이며, 이 Region 에서는 GC 동작이 최적으로 동작하지 않는다.
Available/Unused : 아직 사용되지 않은 Region을 의미한다.
G1 GC에서 중요한 점은, 전통적인 GC의 힙 구조와는 달리 Young 이나 Old 영역이 인접해 있지 않다는 점이다. 이는 영역의 사이즈가 필요에 따라서 동적으로 바뀔 수 있다는 점에서 편리하다.
Adaptive 사이즈가 지원되는 Parallel Old GC와 같은 GC 알고리즘은 각 영역들이 확장될 일을 대비해 여분의 공간을 남겨둔다. 그렇게 함으로서 Young 영역과 Old 영역을 인접한 상태로 둘 수 있다. CMS의 경우엔 자바의 heap 사이즈와 영역의 사이즈를 조정하기 위해서는 Full GC가 필요하다.
반면 G1 GC는 영역의 개념이 물리적으로 존재하지 않고 논리적으로만 존재함으로써 공간과 시간을 아낄 수 있다. (young 영역의 region들과 그 외에는 old 영역의 region들이 있지 이를 물리적 위치로 구분하지 않는다.)
전통적인 GC Layout
Minor GC를 수행할 때 STW가 발생하며, STW 시간을 최대한 줄이기 위해 멀티스레드로 GC를 수행한다. Young GC는 각 Region 중 GC대상 객체가 가장 많은 Region(Eden 또는 Survivor 역할) 에서 수행 되며, 이 Region 에서 살아남은 객체를 다른 Region(Survivor 역할)으로 옮긴 후, 비워진 Region을 사용가능한 Region으로 돌리는 형태 로 동작한다.
Initial Mark (STW 발생)
Old Region에 존재하는 객체들이 참조하는 Survivor Region을 찾는다.
Root Region Scan
Initial Mark에서 찾은 Survivor Region에 대한 GC 대상 객체 스캔 작업을 진행한다.
Concurrent Mark
전체 Heap의 Region에 대해 스캔 작업을 진행하며, GC 대상 객체가 발견되지 않은 Region은 이후 단계를 처리하는데 제외되도록 한다.
Remark (STW 발생)
최종적으로 GC 대상에서 제외될 객체(살아남을 객체)를 식별해낸다.
Cleanup (STW 발생)
살아있는 객체가 가장 적은 Region 에 대한 미사용 객체 제거 수행한다. 이후 STW를 끝내고, 앞선 GC 과정에서 완전히 비워진 Region 을 Freelist에 추가하여 재사용될 수 있게 한다.
Copy (STW 발생)
GC 대상 Region이었지만 Cleanup 과정에서 완전히 비워지지 않은 Region의 살아남은 객체들을 새로운(Available/Unused) Region에 복사하여 Compaction 작업을 수행한다.
https://johngrib.github.io/wiki/jvm-memory/
https://d2.naver.com/helloworld/1329
https://plumbr.io/handbook/garbage-collection-in-java/minor-gc-major-gc-full-gc
https://www.holaxprogramming.com/2013/07/20/java-jvm-gc/
https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html
https://medium.com/@joongwon/jvm-garbage-collection-algorithms-3869b7b0aa6f
https://luavis.me/server/g1-gc (G1 GC)