가비지 컬렉션(Garbage Collection)
1. gc란?
jvm에서 동적으로 할당된 메모리 영역 중 더이상 사용하지 않는 영역을 자동으로 탐지하여 불필요하게 메모리를 차지하지 않도록 제거해주는 역할을 담당한다. 메모리가 가득 차거나 임계점에 도달할 때 발생.
gc의 장단점
- 장점 : 동적으로 할당된 메모리 영역의 전체를 관리할 필요가 없다.
- 단점 : 어떤 메모리를 해제할지 결정하는 비용소모, gc가 발생하는 시점에 프로그램이 일시적으로 정지되기때문 실시간 시스템에 적절하지 않다. 해당 알고리즘은 gc에서 알아서 처리.

2. heap 구성
jvm내 gc가 발생하는 heap영역은 크게 young generation, old generation, permanent 영역으로 나뉜다.
- young generation - eden, survivor1, survivor2 영역으로 나뉘며, 새로 생성한지 얼마 안된 객체들이 존재하는 영역이다. 각 단계에서 메모리 공간이 차게되면 promotion이 발생한다. eden영역에서 발생한 gc를 minor gc라고도 한다.
- old generation - 특정 횟수 이상 참조가 되어 기준 age를 초과한 참조된지 오래된 객체들이 존재하는 곳이다. 이곳에서 발생하는 gc를 major gc 또는 full gc 라고 한다.
permanant generation - (힙과는 별도) jdk1.7 이하 버전에서만 존재하며, jvm에 의해 크기가 고정되는 영역이다.
gc의 대상에서 제외되는 영역이며, 필드와 메서드 정보, 클래스의 메타정보, 런타임 상수풀, static변수,상수 , 메서드의 바이트 코드 등이 존재하는 곳이다(hotspot jvm).
jdk1.8부터는 metaspace영역으로 옮겨지게 되었다.
3. gc 과정
객체가 새로 생성된 후에는 eden영역에 위치하게 되고, 각 영역에 더 이상 공간이 존재하지 않을 경우, survivor1 영역, survivor2 영역으로 차례대로 이동하게된다. 이러한 과정을 promotion이라고 명칭한다. 이 과정에서 survior1 또는 survivor2 영역 중 하나는 꼭 비워져 있어야한다.
hotspot jvm에서 빠른 메모리 할당을 위해서 아래 두가지 기술을 사용한다.
bump the pointer - eden 영역에 마지막 할당된 객체를 추적하여, 새로운 객체 추가시에 할당이 적당할지 확인하기때문에 속도가 비교적 빠르게 할당한다. 다만 멀티 스레드 환경에서는 tread safe 하지 않다.
TLABs(thread local alloction buffers) - 각 스레드별 eden영역의 공간들을 할당받아서, 다중스레드 할당 처리량을 향상 시키는 방법. bump the point 를 사용하더라도 락의 위험성이 없다.
4. gc의 종류
Serial GC
Old 영역의 GC는 mark-sweep-compact 이라는 알고리즘을 사용하는데, 이 알고리즘의 첫 단계는 Old 영역에 살아 있는 객체를 식별(Mark)하는 것이다. 그 다음에는 힙(heap)의 앞 부분부터 확인하여 살아 있는 것만 남긴다(Sweep). 마지막 단계에서는 각 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눈다(Compaction).
Serial GC는 적은 메모리와 CPU 코어 개수가 적을 때 적합한 방식이다.
Pararell GC
Parallel GC는 Serial GC와 기본적인 알고리즘은 같다. 그러나 Serial GC는 GC를 처리하는 스레드가 하나인 것에 비해, Parallel GC는 young generation영역 GC를 처리하는 쓰레드가 여러 개이다. 그렇기 때문에 Serial GC보다 비교적 빠르게 객체를 수집 할 수 있다. Parallel GC는 메모리와 코어의 개수가 여유가 있을때 유리하다.
Parallel Old GC(Parallel Compacting GC)
Parallel Old GC는 JDK 5 update 6부터 제공한 GC 방식이다. Parallel GC와 비교하여 Old 영역의 GC 알고리즘만 다르다. old generation영역에서도 멀티 스레드 방식으로 gc를 수행하며, 이 방식은 Mark-Summary-Compaction 단계를 거친다. Summary 단계는 앞서 GC를 수행한 영역에 대해서 별도로 살아 있는 객체를 식별한다는 점에서 Mark-Sweep-Compaction 알고리즘의 Sweep 단계와 다르며, 약간 더 복잡한 단계를 거친다.
※Compaction단계를 거치기 때문에 단편화를 예방할 수 있다.
Concurrent Mark & Sweep GC(CMS)
1) Initial Mark 단계 - 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는 것으로 끝낸다. stw를 최소화하기 위함이다.
2) Concurrent Mark 단계 - 살아있는 객체에서 참조하고 있는 객체들을 추적하면서 확인한다. 다른 스레드들이 동시에 진행한다.
3) Remark 단계 - Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인한다.
4) Concurrent Sweep 단계 - gc 수집단계 진행. 이 작업또한 다른 스레드가 실행되고 있는 상황에서 진행한다.
이러한 단계로 진행되는 GC 방식이기 때문에 stw 시간이 매우 짧다. 모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용한다.
CMS GC는 stop-the-world 시간이 짧다는 장점에 반해 다음과 같은 단점이 존재한다.
- 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
- Compaction 단계가 기본적으로 제공되지 않는다.※ stw 를 최소화하기 위한 목적에서 , 더 큰 힙을 요구한다.

G1(Garbage First) GC
java7에서부터 포함된 GC이며, java 9의 default GC이다.
전통적인 GC 방법에서 파격적으로 바뀌었다.
바둑판식 배열에 기존의 고정된 영역들이 존재하지 않고 resion이라는 개념으로 eden, survivor, old 영역이 존재하며,
추가로 humongus , available/unused 영역이 추가되었다.
humongus 영역은 resion의 크기를 50% 초과한 객체를 담는 영역이며, available/unused 영역은 사용중, 사용중이지 않은 영역으로 구분된다.
잘게 쪼개진 영역을 병렬적으로 gc 처리되는데, gc를 처리할 rigion이 작다보니 한번 gc에 소요되는 시간을 짧게 여러번에 걸쳐서 진행하여 cms에 비해 stw가 줄어드는것으로 보인다.
5. 변화하게 된 배경
바람직한 gc의 특성을 활용하기 위해서는 trade off를 잘 활용해야한다.
가령 heap메모리의 크기가 작으면, 수집하는데 걸리는 시간이 줄어들지만, stw가 빈번하게 발생하게 되며, heap메모리가 크면, 수집하는데 더 많은 시간이 소요된다. 또한 각기 다른 객체의 수집이 발생하게 되면 그로인한 내부 단편화도 고려대상이다.
성능지표는 처리량, 일시중지시간, 신속성, 수집빈도로 판단하며, 컨셉에따라 gc를 선정한다.
6. static 변수가 gc의 대상이 정말 되지 않을까?
jdk1.8 이상 부터는 static객체는 metaspace 영역으로 옮겨지게 되어 최대한 gc의 대상이 되도록 변경이 되었다.
참고 :
https://www.oracle.com/java/technologies/javase/javase-core-technologies-apis.html
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.5.4
https://d2.naver.com/helloworld/1329
https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/
https://coding-start.tistory.com/206
https://huisam.tistory.com/entry/jvmgc