: 자바의 메모리 관리방법 중 하나로 JVM의 Heap 영역에서 동적으로 할당했던 메모리 영역 중 필요없게 된 메모리 영역을 주기적으로 삭제하는 프로세스
C나 C++에서는 가비지 컬렉션 없이 프로그래머가 수동으로 메모리 할당과 해제를 하나하나 해줘야하지만 Java는 JVM에 탑재되어 있는 가비지 컬렉터가 메모리 관리를 대행해주기 때문에 메모리 관리, 메모리 누수 문제에 대해 완벽하게 관리하지 않아도 개발에만 집중할 수 있다는 장점이 있음
: CG를 실행하기 위해 JVM 애플리케이션 실행을 멈추는 것
stop-the-world
가 발생하면 CG를 실행하는 스레드를 제외한 나머지 모든 스레드가 작업을 멈춤 > CG작업이 완료되면, 중단됐던 작업이 재개
어떤 알고리즘을 사용하더라도 stop-the-world
가 발생하기 때문에, CG성능 최적화는 결국 이 stop-the-world
타임을 줄이는 것을 의미
JVM의 메모리는 Class
Stack
Heap
Native Method
PC
총 5가지로 이루어져 있는데, GC는 이 중 Heap
을 관장함
: 객체들은 실질적으로 Heap영역에서 생성되고 Method Area나 Stack Area 등은 Heap에서 생성된 객체의 주소만 참조하는 형식으로 구성됨
하지만, 이렇게 생성된 Heap의 객체들이 메소드가 끝나는 등의 특정 이벤트들로 인하여 Heap 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제되는 현상이 발생하면 그림처럼 빨간색 객체와 같이 Heap영역 어디서든 참조하고 있지 않은 객체가 발생하게 됨
이 객체들을 Unreachable하다고 하여 주기적으로 가비지 컬렉터가 제거함
Reachable : 객체가 참조되고 있는 상태
Unreachable : 객체가 참조되고 있지 않은 상태 (GC의 대상)
본격 들어가기 전, 짧게 GC의 알고리즘을 살펴보자
- GC-Root로 부터 참조되는 객체와 그 Reachable 객체가 참조하는 객체를 찾아서 Marking
- Marking 과정이 끝나면 Unreachable 객체를 메모리에서 해제(Sweep)
- 살아남은 영역을 조각모음(Compact)하는 Mark and Sweep Compact 알고리즘을 기본으로 사용
메모리가 사용되는지 안되는지를 구분함
위 사진은 참조되고 있는 객체는 파란색, 참조되고 있지 않은 객체는 주황색으로 마킹했음
모든 객체를 하나씩 스캔하여 마킹하기 때문에 오랜 시간을 소모함
이전 단계에서 참조되고 있지 않다고 마킹한 객체를 제거하고 메모리를 반환
Memory Allocator은 반환되어 빈 곳의 주소를 저장해뒀다가 새로운 객체가 선언되면 이 곳에 할당하도록 함
하지만!! 이렇게 하면 메모리가 단편화되어 비효율적으로 사용됨
그래서 이 흩어진 객체들의 영역을 한 곳으로 모으는 과정이 필요함
단편화 : 기억 장치의 빈 공간 또는 자료가 여러 개의 조각으로 나뉘는 현상
이 현상은 기억장치의 사용 가능한 공간을 줄이거나, 읽기와 쓰기의 수행속도를 늦추는 문제점을 일으킴
메모리의 단편화를 최소화하기 위해 참조되는 객체들을 한 곳으로 집약시켜 남은 공간을 극대화 > 새로운 메모리 할당 시 더 쉽고 빠르게 진행 가능
위 까지의 과정을 Mark & Compact
라고 함
하지만 위 처럼 JVM에서 모든 객체를 탐색하며 일일히 참조되고 있는지 안되고 있는지 마킹하고 메모리 해제하고 또 다시 압축하고 완전 복잡함 복잡하면 뭐다? 비효율적이다~
GC는 에이징과 함꼐 세대별 청소를 사용해 힙 영역의 공간을 최대한 확보
root set으로부터 접근 불가능한 모든 객체들은 쓰레기로 간주돼 GC에 의해 수집되는데 GC가 수행될 때마다 하나하나 추적하는 것은 부하가 큰 일
하지만!! 아래 그래프처럼 대부분의 응용프로그램에서 객체들이 짧은 시간동안만 살아 있다는 것을 확인
이를 약한 세대 가설이라고 함
위 그래프는 대부분의 응용 프로그램들의 object 수명을 나타낸 그래프
이 결과를 통해 대부분의 객체들은 생성된지 얼마 안 가 소멸한다는 것을 알 수 있음
신규로 생성한 객체의 대부분은 머지않아 사용하지 않는 상태가 되고, 오래된 객체에서 신규 객체로의 참조는 매우 적게 일어난다는 가정이 바로 Week Generational Hypothesis
이러한 가설 아래 Young영역과 Old영역으로 메모리를 분할해 새롭게 생성되는 객체는 Young영역에 보관하고, 생성된지 시간이 지난 그러니깐 점차 나이를 먹은 객체는 Old영역에 보관하는 알고리즘이 고안됨
약한 세대 가설을 최적화하기 위해 Java의 메모리는 아래처럼 세대 단위로 관리
어떤 세대의 메모리 풀이 가득 찬다면 해당 세대에서만! GC가 실행
GC가 수행된 후 남은 객체들은 메모리의 오른쪽(Old Generation)으로 이동
이것을 promoted 또는 tenured라고 함
young Generation은 빨리 모이기 때문에 여기에 관한 GC는 속도를 중시함
반번, Old Generation 관련 알고리즘들은 공간 효율을 중시함
~~왜냐하면 대부분 Old Generation은 힙의 대다수 부분을 차지하니깐
새롭게 생성한 객체들의 대부분은 여기에 자리잡음
약한 세대 가설(Week Generational Hypothesis)에 따르면, 대부분 객체가 금방 접근 불가능한 상태(Unreachable)가 되기 때문에 매우 많은 객체가 이 영역에서 생성됐다가 사라졌다가 함
해당 영역에서 객체가 메모리 해제되는 경우를 Minor GC가 발생했다고 말함
금방 접근불가 상태가 된 애들과 달리 오랫동안 메모리에 남아있던 객체들이 이 영역으로 복사됨
대부분 Young영역보다 크게 할당하며 크기가 큰 만큼 GC는 Young영역에 비해 적게 발생
해당 영역에서 객체가 메모리 해제되는 경우를 Major GC가 발생했다고 말함
Method Area라고도 부르는 이 영역은 JVM이 클래스들과 메소드들을 설명하기 위해 필요한 다양한 메타 데이터들을 포함하고 있음
JDK8 부터는 Metaspace로 교체
Eden Space
에 할
Eden 최초 생성된 객체를 위한 공간
Eden Space
가 가득차면 Minor GC수Eden Space
가 클리어 될 때 같이 반환 (바이바이~)비 참조 객체는 사라지고, 참조 객체는 마찬가지로 Survivor영역으로 이동
하지만 이번에는 두 번째 영역인 S1영역으로 이동함
또한, 최근 Minor GC에서 S0영역으로 이동된 객체들도 Age가 증가하고,
S1영역으로 이동
모든 객체들이 S1영역으로 이동하게되면 Eden Space와 S0영역 모두 클리어
하지만 이번 과정에서는 Survivor 영역이 서로 스위치!
참조되는 객체들은 S0로 이동하고, 살아남은 객체들은 Age가 증가함
그리고 Eden Space와 S1영역은 클리어!
(아래 그림의 Age Threshold : 8살)
Minor GC가 발생하면서 살아남은 객체는 Aging되고, 일정 Age threshold 값을 넘게되면 Old영역으로 할당된다는 것을 기억하기
GC의 메모리 해제 과정에는 Marking, Deleting, Compacting 이렇게 크게 3가지가 있으며, 좀 더 효율적으로 하기 위해 Heap영역을 Young, Old, Permanent로 나눔
중요한 것은
GC가 수행될 때 일시적으로 애플리케이션 수행이 정지되며, Stop-the-World의 시간을 줄이는게 핵심
참고
https://docs.oracle.com/en/java/javase/16/gctuning/garbage-collector-implementation.html#GUID-71D796B3-CBAB-4D80-B5C3-2620E45F6E5D
https://velog.io/@haero_kim/Garbage-Collection-%EA%B8%B0%EC%B4%88-%EA%B3%B5%EB%9E%B5%ED%95%98%EA%B8%B0
https://brorica.tistory.com/m/entry/%EC%9E%90%EB%B0%94-gc
https://reference-m1.tistory.com/107
https://learn.microsoft.com/ko-kr/dotnet/standard/garbage-collection/performance