메모리 관리 기법 중 하나이다.
프로그램이 동적으로 할당했던 메모리 영역(Heap) 중 필요 없게 된 영역을 알아서 해제하는 기법이다.
JVM의 Heap영역에서 사용하지 않는 객체를 삭제하는 프로세스이다.
C, C++의 경우 Heap영역의 메모리를 관리하기 위해 코드레벨에서 할당받고, 해제해야 했다.
할당받은 메모리 영역을 제대로 해제하지 않아 Memory Leak문제가 발생할 수 도 있다.
장점
- 개발자의 실수로 인한 메모리 누수를 막을 수 있다.
- 해제된 메모리에 접근하는 오류를 막을 수 있다.
- 해제된 메모리를 또 해제하는 이중 해제를 막을 수 있다.
단점
- 어떤 메모리 영역이 해제의 대상이 될지 검사하고 해제하는 일은 프로그램이 해야할 일을 못하도록 방해하는 순수한 오버헤드이다.
- 개발자가 GC가 정확하게 언제 메모리를 해제하는지 알 수 없다. 실시간성이 강하게 강조되는 프로그램의 경우 GC에게 메모리 관리를 맡기는 것이 좋지 않을 수도 있다.
GC root(=Root space)에서부터 각각 참조하고 있는 객체들을 하나씩 탐색해 나간다.
- Root Space : 스택 변수, 전역 변수 등 Heap영역 참조를 담은 변수이다.
참조되는 객체들을 reachable하다 하고,
참조되고 있지 않은 객체들을 unreachable하다 한다.
Heap영역은 Old Generation과 Young Generation으로 구분이 된다.
- Young Generation : 새로운 객체들이 할당되는 영역
- Old Generation : Young Generation에서 오랫동안 살아남은 객체들이 존재하는 영역
- MetaSpace : Heap영역에 있는 건 아니다. GC시에 필요한 클래스, 메소드의 요약 정보가 존재하는 영역
스택영역, 메소드영역, native method stack, Heap영역에 있는 객체들에 의해 참조되고 있는 객체들은 reachable하다고 판단한다.
어느 곳의 객체에서도 참조되고 있지 않은 객체를 unreachable하다 하고 GC의 수거 대상이 된다.
Heap영역에 선언된 객체들이 각각 reference count라는 별도의 숫자를 가지고 있다.
reference count는 몇 가지 방법으로 해당 객체에 접근할 수 있는지를 뜻한다.
reference count가 0이 되면(=해당 객체에 접근할 수 있는 방법이 없다) GC의 대상이 된다.
순환참조 문제
Root Space에서 Heap Space접근을 모두 끊게 되면 Heap Space안에서 서로가 서로를 참조하는 객체들이 남게 되고 이들이 점유하고 있는 사용하지 않는 메모리 영역이 해제되지 못하고 Memory Leak이 발생한다.
Mark And Sweep의 특징
- 의도적으로 GC를 실행시켜야 한다.
- 어플리케이션 실행과 GC실행이 병행된다.
Root Space 로부터 그래프 순회를 통해 모든 변수를 스캔하면서 각각 어떤 객체를 참고하고 있는지 마킹하는 과정이다.
즉, reachable한 객체와 unreachable한 객체를 식별하는 과정이다.
Unreachable한 객체들을 heap에서 제거하는 과정이다.
알고리즘 과정에 따라서 추가되기도 하는 과정이다.
Sweep 후에 띄엄띄엄 남아있는 객체들을 한 곳에 모아서 메모리단편화를 막아주는 작업이다.
GC가 한번 일어날 때마다 Mark와 Sweep이 수행되고 어쩔 때는 compact과정까지 수행된다.
Eden, Survivor0, Survivor1 으로 구성된다.
- Survivor영역의 규칙 : Survivor0에 객체가 존재하면, Survivor1은 무조건 비워줘야 한다. 반대로 Survivor1에 객체가 존재하면 Survivor0은 무조건 비워줘야 한다.
설계자들이 다음과 같은 이유를 기반으로 GC를 설계하였다.
Weak Generational Hypothesis
- 대부분의 객체는 금방 unreachable한 상태가 된다. 즉, 금방 garbage가 된다.
- 오래된 객체에서 젋은 객체로의 참조는 아주 적게 존재한다.
실제로 Minor GC가 한 번 일어나면 상당 수의 객체들이 수거된다. 오히려 Minor GC가 자주 일어나는 것이 메모리 낭비를 막을 수 있다.
그렇기 때문에 young Generation을 Eden과 두 개의 Survivor영역으로 나눠 놓은 것이다.
반면에 Major GC는 한 번 일어나도 수거되는 객체들이 별로 없다.
GC를 실행하면 GC를 실행하는 스레드외의 모든 작업을 중단하는 현상이다.
GC종류에 따라서 Stop the World가 발생하는 시간이 다르다.Stop-the-World시간을 줄이는 것이 어려운 최적화 작업이었다.
- Serial GC
- Parallel GC
- Parallel Old GC
- CMS GC(Concurrent Mark Sweep)
- G1 GC(Garbage First)
성능 개선의 최종단계이다.
객체 생성 자체를 줄이려는 코드 레벨에서의 노력이 선행되어야 한다.
- Old Generation으로 넘어가는 객체를 최소화하기(Major GC 적게 발생시키기)
- Major GC 시간을 짧게 유지하기
Major GC가 Minor GC보다 Stop-the-World시간이 길기 때문에 위의 목표가 설정되는 것이다.
한정된 Heap영역에 Young Generation과 Old Generation을 각각 얼마만큼 할당하는 것이 적당한지를 판단해야 한다.
- 현재 GC 상태를 모니터링한다.
- 어플리케이션 특성에 알맞은 GC 방식과 메모리 크기를 설정한다.
- 옳다고 판단되면 적용한다.
jstat -gcutil -t (프로세스번호) (몇초에 얼마)
위의 명령어를 통해 관련된 정보를 출력하게 할 수 있다.