JVM의 Heap 영역에서 다른 객체를 참조하지 않고 더이상 사용되지 않은 메모리를 찾아 삭제하는 작업을 말한다.
Heap의 메모리 구조는 Young Generation, Old Generation, Permanent Generation 으로 구성되어 있다.
*JVM 구조 중 가장 많이 사용되고 있는 Hotspot JVM 구조를 가져왔다.
위 영역들은 대략적으로 보여주는 구조이고 GC 마다 다르게 할당될 수 있으며 자세한 내용은 아래 GC 종류에서 다루겠다.
Stop The World란 GC 작업이 수행되는 동안 GC를 수행하는 스레드를 제외한 모든 스레드들이 일시적으로 동작을 멈추는 상태를 말한다.
즉, GC 작업을 통해 자주 메모리를 비워주는 것이 좋을 수만은 없고 그렇다고 GC 작업의 빈도수를 줄이는 것은 메모리 영역에 부담을 줄 수 있기 때문에 적절한 빈도로 작동하게 하는 것이 중요하다.
❔ System.gc()란?
말 그대로 GC 작업을 실행시키는 메서드이다.
JVM에서 GC 작업은 자동으로 수행되지만 추가로 작업이 수행되게 하고 싶은 경우 사용된다.
앞서 말했다시피 GC 작업이 수행되는 동안에는 Stop The World가 일어나기 때문에 프로그램 작업에 무리를 줄 수 있고 성능에 저하를 주기 때문에 신중하게 사용해야 한다.
System.gc()를 실행시켜 테스트를 해보았을 때 시간이 훨씬 많이 걸리는 것이 확인된다.
JDK 5, 6에서 사용되며 싱글 스레드로 수행되는 가장 간단한 방식의 GC이다.
Serial GC 특징
JDK 8의 Default GC 방식으로 멀티 스레드로 GC 작업이 수행된다.
Parallel GC 특징
Parallel GC는 싱글 스레드로 GC가 수행되는 시간에 초점을 두는 방식이고 CMS GC는 GC가 수행되는 동안 Stop The World 시간을 최소화하는데에 초점을 둔 방식이다.
이름 그대로 다른 스레드들과 동시에 수행되며(Concurrent) 객체들을 마킹(Mark)하고 삭제하는 작업(Sweep)이 수행된다.
JDK9 이후로는 사용되지 않으며 JDK14부터는 지원이 종료되었다.
CMS GC 특징
CMS GC는 다음과 같은 작업을 수행한다.
Initial Mark
살아있는 객체를 마킹하는 단계로, 클래스 로더에서 살아있는 객체를 찾다가 찾으면 바로 끝낸다. 그렇기 때문에 Stop The World 시간이 매우 짧다.
Concurrent Mark
Initial Mark 단계에서 찾은 살아있는 객체를 따라가면서 여러 객체들을 확인한다.
이 단계는 다른 작업중인 스레드와 동시에 이뤄지기 때문에 Stop The World 가 이뤄지지 않는다.
Remark
Concurrent Mark 단계에서 확인한 객체들중 참조가 끊긴 객체, 참조하고 있는 객체를 탐색하여 참조가 끊긴 객체만 식별한다.
객체를 식별하는 단계로 Stop The World 가 발생한다.
Concurrent Sweep
삭제할 객체들을 처리한다.
해당 작업은 다른 스레드들과 같이 수행되기 때문에 Stop The World가 발생하지 않는다.
위와 같은 단계로 수행되기 때문에 Stop The World 시간이 매우 짧아 성능을 높일 수 있다.
동시에 여러 스레드로 작업이 수행되어 다른 GC 방식보다 많은 메모리와 CPU를 사용한다.
Compaction 단계가 기본적으로 제공되지 않는다.
Compaction이란?
Compaction이란 객체들중에서 참조되지 않은 객체들만 삭제하다보면 메모리 영역 사이에 빈 공간이 생기게 되어 그 빈 공간을 제거하기 위해 메모리를 압축하여 제거하는 작업이다.
CMS GC에서는 기본적으로 제공하지 않기 때문에 수동으로 작업을 처리해줘야하는데 Compaction 과정에서 임의의 객체의 주소가 변경되거나 등의 작업이 수행되어야하기 때문에 Stop The World 가 발생한다.
조각난 메모리가 많기 때문에 Compaction 작업을 수행하게 된다면 Stop The World 시간이 길어져 CMS GC에서 줄인 Stop The World 시간이 의미없게 될 수도 있다.
그렇기 때문에 Compaction 과정이 얼마나 자주, 오랫동안 수행되는지 확인해줘야한다.
JDK9의 default GC로 메모리와 CPU를 많이 차지하는 CMS GC 문제점을 개선한 방식이다.
기존 Heap 영역을 구분하는 방식과 다르게 Heap 영역을 바둑판처럼 임의의 region으로 나누고 에서 살아있는 객체가 적게 들어있는 region부터 GC 작업을 수행하는 방식이다.
G1 GC 특징
앞의 GC 들은 Heap 영역을 Young 영역과 Old 영역으로 나눠 GC 작업이 수행되었지만 G1 GC는 Heap을 여러 region으로 나누고 각 region들은 각자의 역할을 동적으로 할당받아 수행한다.
<Heap region 할당 과정>
할당되기전 Heap 영역은 고정된 크기의 영역으로, 하나의 메모리 영역이다.
Java가 실행될 때 JVM은 Heap의 영역 크기를 설정하여 region 별로 나눈다.
몇 개의 region을 Eden 영역으로 지정하고 각 Eden 영역에 새로운 객체들이 할당되다가 Eden 객체가 가득차면 GC가 수행된다.
GC 수행 후 살아남은 객체들은 Survivor 영역으로 이동되고 이러한 과정을 오래 반복하면서 살아남은 객체들은 Old 영역으로 이동된다.
이전 Heap과는 다르게 각 영역들이 고정된 사이즈가 아니고 연속적이지 않기 때문에 각 객체의 크기에 따라 메모리 할당을 할 수 있어 메모리 부담이 적다.
G1 GC 동작 과정
JDK11부터 실험적으로 도입된 GC로 메모리 구조를 여러 사이즈의 Z Page 영역으로 나눠 메모리를 관리하는 방식이다.
Z GC 특징
Z GC 구조
Z GC는 Heap 영역을 Z Page 영역으로 나누고 다양한 사이즈로 할당된다.
Z GC의 Compaction 방식은 삭제되고 난 후의 빈 곳을 찾아 채워넣는 것이 아닌 새로운 영역을 생성해서 살아있는 객체들을 채우는 방식이다.
즉, GC가 실행될 때 마킹된 객체들을 새로운 영역을 만들어 집어넣고 기존 꽉 찬 영역은 GC가 수행되어 삭제된다.
Java의 특징 중에 GC 처리를 자동으로 해준다는 것이 있다. C 언어만 해도 메모리 관리를 직접 해줘야하는데 그렇다면 GC 처리라는 것이 어떤 것인지 어떻게 수행되는 것인지 알 필요가 있다.
또한, GC 작업은 메모리의 성능과 직접 관련이 있고 JDK 버전 별로도 수행되는 GC 방식이 다르기 때문에 JDK 버전 선택 시에도 참고하고 그 후 개발할 때에도 고려할 수 있는 개발자가 되어야 하며 매우 중요한 부분이다.
출처
https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/
https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#gc()
https://blog.gceasy.io/2019/06/30/who-why-what-fix-system-gc/
https://d2.naver.com/helloworld/1329
https://stackoverflow.com/questions/24587255/what-is-a-compaction-in-java-gc
https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html
https://www.oracle.com/technical-resources/articles/java/g1gc.html
https://thinkground.studio/일반적인-gc-내용과-g1gc-garbage-first-garbage-collector-내용/
https://medium.com/lucky-sonnie/java-garbage-collection에-대해-ae5b6ac70b9d
https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html
https://d2.naver.com/helloworld/0128759