Java - GC (Garbage Collection)
1. GC
- Java에서 GC는 우리가 개발에만 집중할 수 있게 알아서 Heap 메모리를 관리해준다.
- 더 이상 사용하지 않는 메모리를 체크하고 삭제하는 역할을 한다.
2. GC의 단점
- 자동 처리이기 때문에 언제 메모리에서 해제되는지 명확하게 알 수 없기에 제어하기 힘들며, GC가 동작하는 동안에는 GC관련 Thread를 제외한 다른 모든 Thread가 동작을 멈추기 때문에 오버헤드가 발생할 수 있다.
- 이걸 Stop The World 라고 한다.
- 또한 이러한 이유로 우리는 System.gc()와 같은 명령어를 사용하면 안된다.
3. GC 영역과 GC 발생 순서
- Heap 메모리 안에는 Young Generation과 Old Generation이 있는데, Young은 말 그대로 할당된지 얼마 안되는 객체들이 있으며, Old 영역은 오래 살아남은 객체들이 존재한다.
- Young 영역에 대한 GC를 Minor GC라고 하고, Old 영역에 대한 GC를 Major GC 혹은 Full GC 라고 한다.
- Young 영역이 Old 영역보다 GC가 훨씬 자주 발생한다.
- Young 영역 안에는 Eden과 Survivor0, Survivor1 이 있다.
- 처음에는 Eden에 저장되고, 첫번째 Minor GC에 사라지지 않으면 Survivor0 혹은 1중에 비어있는 곳에 저장되며 해당 객체의 age가 1 상승한다.
- 그리고 다음 Eden에 새로운 객체들이 들어올 것이고, Eden과 현재 객체들이 있는 Survivor의 객체들을 대상으로 Minor GC가 또 동작한다.
- 여기서 사라지지 않은 Eden과 Survivor 속 객체들은 age가 1 상승하며 비어있는 Survivor로 모여서 옮겨진다. ( 아까 Survivor 0이었으면 이번엔 1로)
- 그러다가 age가 임계점에 다다른 객체는 Old Generation 으로 옮겨진다. ( 보통은 6비트로 31이 임계값이다. )
4. GC 알고리즘
- Serial GC : 실무에서 잘 사용하지 않는.. 싱글 쓰레드 방식의 GC 알고리즘 ( stop the world 가 오래 걸림 )
- 성능이 안좋아서 CPU 코어가 1개인 저사양 환경에서 테스트할 때 정도만 사용.
- Paraller GC
- Minor GC는 멀티스레드, Major GC는 싱글스레드로 동작하는 Java8의 디폴트GC
- 멀티스레드다 보니 stop the world가 Seiral GC보다는 짧음.
- Parallel Old GC
- Paraller GC의 업그레이드 버전으로 Major GC도 멀티스레드로 동작
- CMS GC
- top the world의 시간은 줄이지만, CPU사용량이 높고 GC과정이 복잡하여 Java9부터는 사용을 권하지 않으며 14부터는 사용이 중지된 알고리즘
- G1 GC
- CMS를 보완하여 Java 7부터 도입되었으며 Java 9부터는 디폴트 GC임.
- 기존의 Eden Survivor Old 구조가 아닌 체스판 형식으로 분할하여 상황에 따라 동적으로 GC를 일으킴.
- 쓰레기값이 가득찬 영역을 빠르게 회수하여 빈 공간을 확보하므로 성능이 좋음.
- 그리고 뛰어난 병렬성이 있음.
- 그러나, Heap이 4GB 이상일때 권장되며 Heap용량이 작을 경우에는 권장하지 않음.
- Shenandoah GC
- Java 12에 생겼으며, 강력한 Concurrency와 가벼운 GC 로직으로 heap 사이즈에 영향을 받지 않고 일정한 pause 시간이 소요가 특징. 기존 CMS의 단편화와 G1가 가진 pause의 이슈를 모두 해결한 알고리즘
- ZGC
- Java 15에 생겼으며, 대용량 메모리를 잘 처리하기 위한 알고리즘.
- 강한 장점으로는 힙 크기가 아무리 증가하여도 stop the world의 시간이 절대 10ms를 넘지 않음.
5. 결론
- 기본적으로 Java8, 11, 17을 많이 쓰는데 8에선 Parallel GC / 11이후는 G1 GC를 주로 사용하고 있다고 보면 된다.