Garbage Collection
JVM의 Heap 영역 내에서 동적으로 할당했던 메모리 중 더 이상 사용하지 않는 메모리 객체들을 모아 주기적으로 자동으로 제거해주는 하나의 프로세스
개발자가 메모리를 직접 할당하고 해제하는 일에 신경을 쓸 필요가 없어져 개발에만 집중할 수 있게 해준다.
GC는 만능일까?
세상에 완벽한 것은 없기 때문에 당연히 단점이 존재한다.
우선 GC는 개발자가 제어할 수 있는 영역이 아니기 때문에 GC가 메모리를 언제 해제하는지 알 수 없으며,
GC가 동작하는 동안 관련 쓰레드를 제외한 모든 쓰레드가 멈추기(Stop The World) 때문에 오버헤드가 발생한다.
이런 STW 현상은 GC가 과하게 실행된다면 성능 하락으로 이어질 수도 있기 때문에
효율적으로 GC를 실행할 수 있게 최적화 하는 GC 튜닝 작업이 필요하다.
GC는 어떤 것들을 정리할까?
GC는 객체가 참조되어 접근할 수 있는지에 대한 도달성이라는 개념을 적용하여
객체의 레퍼런스가 존재하면 Reachable, 그렇지 않다면 Unreachable로 구분해 수거한다.
사실 당연한 이야기지만 참조가 되고 있지 않다면 앞으로 사용할 수 없는 객체이기 때문에
더 이상 필요 없는게 맞고 삭제하는게 당연하다.
GC의 청소 원리
청소는 Mark And Sweep 알고리즘으로 이루어진다.
청소할 객체를 식별한 후에 제거하면 제거된 공간이 빌 것이고
빈 공간을 앞에서부터 남아있는 객체들로 다시 채워나가는 방식으로 진행된다.
(Mark > Sweep > Compact)
우선 Mark 과정은 그래프 순회로 동작하기 때문에 그래프 탐색을 알고 있으면 이해하기 쉬운데
RootSpace부터 연결되어 갈 수 있는 모든 객체들까지 탐색하면서 마크를 해두면
연결되지 않은, 즉 참조되고 있지 않은 객체들은 마크가 되지 않기 때문에 삭제 대상이 될 것이다.
다음으로 Sweep 과정은 위에서 마크되지 않은 Unreachable 객체들을 Heap에서 제거한 후에
Compact 과정을 통해 삭제되지 않고 분산되어 떨어져 있는 객체들을 Heap의 시작주소로 모아 압축한다.
여기서 RootSpace는 Heap 영역을 참조하는 메서드 영역, 스태틱 변수, 스택, 네이티브 메서드 스택가 포함된다.
Heap 영역
GC이 대상인 Heap 영역은 Weak Generational Hypothesis를 이용해 설계되었는데
이는 대부분의 객체는 금방 Unreachable 상태가 되고, 오래된 객체에서 새로운 객체로의 참조는 적다는 점이다.
객체는 한 번 사용하기 위해 만들어지는 것이 대부분이고, 메모리에 오래 남아있지 않다는 것인데
Heap 영역은 이러한 관점에서 설계되 Young과 Old 영역으로 나누어 구성되었다.
Young Generation(Minor GC) / Old Generation(Major(Full) GC)
Young 영역은 새로 생성된 객체들이 할당 되고 대부분의 객체가 금방 Unreachable 상태가 되는 것처럼
대부분의 객체가 해당 영역에서 생성되었다가 사라지며, 이 영역에 대한 GC를 Minor GC라고 부른다.
Old 영역은 Young 영역에서 Reachable 상태를 오래 유지한 객체가 복사되는 영역으로
Young 영역보다 큰 영역이 할당된만큼 Garbage는 적게 발생하며, 해당 영역에 대한 GC를 Major GC라고 부른다.
대체로 수명이 짧은 객체들은 큰 공간을 사용하지 않기 때문에 수명이 긴 객체들이 저장되는 Old 영역이 더 크다.
Eden / Survivor0, 1
Young 영역은 Eden, Survivor0, Survivor1 세 가지 영역으로 다시 나누어진다.
Eden 영역은 new를 통해 새로 생성된 객체가 존재하며 이곳에서 정기적인 GC를 거친 후에
살아남은 객체들은 Survivor 영역으로 보내지게 된다.
Survivor 영역은 최소 1번 이상의 GC를 거친 후에 살아남은 객체가 존재하는 영역으로
0과 1 영역 중 하나는 무조건 비어 있어야 한다는 규칙이 있다.
힙 영역을 세분화하여 객체를 생존 기간별로 섬세하게 제어할 수 있어 GC를 정확하게 실행할 수 있게 된다.
Minor GC
Minor GC는 Old 영역에 비해 작은 Young 영역에서 객체를 찾고 제거하기 때문에 상대적으로 적은 시간이 소요된다.
우선 모든 객체들은 처음 생성될 때 Young 영역의 Eden 영역에 위치하게 되고, 해당 영역이 가득차면
Minor GC가 실행되어 Mark 과정을 통해 Reachable 객체를 Survivor 영역으로 이동시키고
Eden 영역의 Unreachable 객체의 메모리를 Sweep 하고 이러한 과정들은 반복한다.
이 때 살아남은 모든 객체들의 age를 1씩 증가시켜 GC에서 살아남은 횟수를 기록하게 되고
이 age 값이 임계값에 다다르면 Old 영역으로 이동하게 된다.
Major GC
Minor GC가 Eden 영역의 메모리가 부족할 때 실행되는 것처럼 Major GC도 Old 영역의 메모리가 부족하면 이루어진다.
객체의 age가 임계값에 이르러 Old 영역으로 이동하는 Promotion이 반복되서 진행되다
Old 영역의 메모리가 부족한 상황이 발생하면 Major GC가 실행되게 되고 참조되지 않는 객체들을 제거한다.
하지만 Old 영역에서의 GC는 큰 공간을 탐색하며 Unreachable 객체를 제거하기 때문에 오랜 시간이 소요되어
Stop The World 문제가 발생해 관련 없는 모든 쓰레드가 멈춘 후에 Mark and Sweep 작업이 수행된다.
'Java > Notion' 카테고리의 다른 글
자바의 버전별 특징 (0) | 2024.03.04 |
---|---|
JVM Warm Up (0) | 2024.03.04 |
JVM 뜯어보기 (컴파일, 인터프리팅, JIT, GC, 메모리) (0) | 2024.03.02 |
자바의 컴파일 과정 (0) | 2023.10.25 |
BigInteger (0) | 2023.05.08 |