[Java] Garbage Collection - 개념, 구조, GC 종류(Serial GC, Parallel GC, CMS GC, G1 GC, Z GC)

Erin Lee·2023년 12월 4일
1

Garbage Collection

1. Garbage Collection이란?


JVM의 Heap 영역에서 다른 객체를 참조하지 않고 더이상 사용되지 않은 메모리를 찾아 삭제하는 작업을 말한다.
Heap의 메모리 구조는 Young Generation, Old Generation, Permanent Generation 으로 구성되어 있다.
*JVM 구조 중 가장 많이 사용되고 있는 Hotspot JVM 구조를 가져왔다.

  • Young Generation Young 영역은 Eden, Survivor Space 구조로 나뉜다. Young 영역에서 일어나는 GC를 MinorGC 라고 한다.
    • Eden : 새로 생성된 객체의 대부분이 Eden 영역에 저장된다. Eden 영역에서 GC가 일어난 이후 살아남은 객체는 Survivor Space로 이동된다.
    • Survivor Space : 두 개의 구조로 나뉘어져 있으며 하나의 Survivor 영역이 가득차게 되면 GC가 일어난 후 살아남은 객체들은 다른 Survivor 영역으로 이동된다. 그리고 기존 Survivor 영역에 저장된 객체들은 모두 삭제가 된다.
  • Old Generation Young 영역에서 살아남은 객체들이 복사되는 영역으로 Young 영역보다 크게 메모리가 할당되며 이 영역에서 일어나는 GC를 MajorGC라고 한다.
  • Permanent Generation 클래스 로더에 의해 로드된 클래스와 관련된 메타데이터를 저장하는 영역이다. 이 메타데이터는 클래스의 구조, 상태, 메소드, 필드 정보 등을 포함하고 있다. 예를 들어, 클래스의 바이트 코드, 메소드 테이블, 필드 테이블 등이 여기에 속한다. JDK 8 이전에는 Heap의 영역으로 속해있었으나 Method Area를 대표하기도 하여 혼동이 있었다. 하지만 JDK 8 이후 부터 Metaspace로 대체가 되면서 Heap 영역으로부터 분리가 되었고 Permanent Generation은 사라졌다.

위 영역들은 대략적으로 보여주는 구조이고 GC 마다 다르게 할당될 수 있으며 자세한 내용은 아래 GC 종류에서 다루겠다.

2. Garbage Collection 특징


  • Heap 영역에 있는 메모리들을 탐색한다.
  • GC 작업이 수행될 때 ‘Stop The World’가 일어난다.

Stop The World란?

Stop The World란 GC 작업이 수행되는 동안 GC를 수행하는 스레드를 제외한 모든 스레드들이 일시적으로 동작을 멈추는 상태를 말한다.

즉, GC 작업을 통해 자주 메모리를 비워주는 것이 좋을 수만은 없고 그렇다고 GC 작업의 빈도수를 줄이는 것은 메모리 영역에 부담을 줄 수 있기 때문에 적절한 빈도로 작동하게 하는 것이 중요하다.

System.gc()란?
말 그대로 GC 작업을 실행시키는 메서드이다.
JVM에서 GC 작업은 자동으로 수행되지만 추가로 작업이 수행되게 하고 싶은 경우 사용된다.
앞서 말했다시피 GC 작업이 수행되는 동안에는 Stop The World가 일어나기 때문에 프로그램 작업에 무리를 줄 수 있고 성능에 저하를 주기 때문에 신중하게 사용해야 한다.

System.gc()를 실행시켜 테스트를 해보았을 때 시간이 훨씬 많이 걸리는 것이 확인된다.

3. Garbage Collection 종류


Serial GC(직렬 GC)

JDK 5, 6에서 사용되며 싱글 스레드로 수행되는 가장 간단한 방식의 GC이다.

Serial GC 특징

  • 싱글 스레드로 작업이 수행되기 때문에 GC 작업 시간이 길다.

  • Mark-Sweep-Compact 알고리즘을 사용한다.
    Mark-Sweep-Compact 알고리즘이란 단어 그대로 식별하고 청소하고 채우는 알고리즘이다.

factorio thumbnail

  1. Mark - Heap의 Old 영역에서 GC 대상 외 살아남을 객체를 Marking하는 단계이다. unmarked 할 대상과 marked 대상을 식별한다.
  2. Sweep(Normal Deletion) -Marking 되어있지 않은 객체들을 앞에서부터 삭제한다. 해당 작업이 완료가 되면 Marking 정보를 초기화한다.
  3. Compact - Heap의 앞부분부터 객체들이 삭제되고 생긴 살아남은 객체들 사이의 비워진 공간이 생기지 않게 객체로 채워넣는다.

Parallel GC(=Throughput GC)

JDK 8의 Default GC 방식으로 멀티 스레드로 GC 작업이 수행된다.

Parallel GC 특징

factorio thumbnail

  • Serial GC와 사용되는 알고리즘은 동일하지만 멀티 스레드로 작업이 수행되어 GC 수행 시간이 짧다. → Stop The World 시간이 짧다.
    Old 영역에서는 싱글 스레드로 수행되고 Young 영역에서는 멀티 스레드로 수행된다.

Concurrent & Mark & Sweep GC(CMS GC)

Parallel GC는 싱글 스레드로 GC가 수행되는 시간에 초점을 두는 방식이고 CMS GC는 GC가 수행되는 동안 Stop The World 시간을 최소화하는데에 초점을 둔 방식이다.
이름 그대로 다른 스레드들과 동시에 수행되며(Concurrent) 객체들을 마킹(Mark)하고 삭제하는 작업(Sweep)이 수행된다.
JDK9 이후로는 사용되지 않으며 JDK14부터는 지원이 종료되었다.

CMS GC 특징

factorio thumbnail

  • CMS GC는 다음과 같은 작업을 수행한다.

    1. Initial Mark
      살아있는 객체를 마킹하는 단계로, 클래스 로더에서 살아있는 객체를 찾다가 찾으면 바로 끝낸다. 그렇기 때문에 Stop The World 시간이 매우 짧다.

    2. Concurrent Mark
      Initial Mark 단계에서 찾은 살아있는 객체를 따라가면서 여러 객체들을 확인한다.
      이 단계는 다른 작업중인 스레드와 동시에 이뤄지기 때문에 Stop The World 가 이뤄지지 않는다.

    3. Remark
      Concurrent Mark 단계에서 확인한 객체들중 참조가 끊긴 객체, 참조하고 있는 객체를 탐색하여 참조가 끊긴 객체만 식별한다.
      객체를 식별하는 단계로 Stop The World 가 발생한다.

    4. 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 과정이 얼마나 자주, 오랫동안 수행되는지 확인해줘야한다.


Garbage First GC(G1 GC)

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 할당 과정>

    1. 할당되기전 Heap 영역은 고정된 크기의 영역으로, 하나의 메모리 영역이다.

      factorio thumbnail

    2. Java가 실행될 때 JVM은 Heap의 영역 크기를 설정하여 region 별로 나눈다.

      factorio thumbnail

    • Young Generation
    • Eden : 새로운 객체가 할당받은 영역으로 이전 GC와 다르게 연속적이지 않는다.
    • Survivor : Eden 영역에서 살아남은 객체가 복사 또는 이동되는 영역
    • Old Generation - Young 영역에서 살아남은 객체가 할당되는 영역
    • Humongous - region의 크기가 50%가 넘는 큰 객체를 저장하기 위한 영역
    • Available / Unused - Heap에서 아직 사용되지 않는 영역

    몇 개의 region을 Eden 영역으로 지정하고 각 Eden 영역에 새로운 객체들이 할당되다가 Eden 객체가 가득차면 GC가 수행된다.
    GC 수행 후 살아남은 객체들은 Survivor 영역으로 이동되고 이러한 과정을 오래 반복하면서 살아남은 객체들은 Old 영역으로 이동된다.

  • 이전 Heap과는 다르게 각 영역들이 고정된 사이즈가 아니고 연속적이지 않기 때문에 각 객체의 크기에 따라 메모리 할당을 할 수 있어 메모리 부담이 적다.

G1 GC 동작 과정

  1. Young GC
    Eden, Survivor 영역에서 수행되며 Eden에서 GC 이후 살아있는 객체를 Survivor 영역으로 이동시키고 비워진 Eden 영역은 Available 영역으로 변경된다.
  2. Full GC

    factorio thumbnail

  • Initial Mark
    Old Region에 남아있는 객체들이 참조하고 있는 Survivor Region을 찾는다. 이때 Stop The World가 발생한다.
  • Root Region Scan
    Initial Mark 단계에서 찾은 Survivor Region에서 GC 작업 대상이 있는지 확인한다.
  • Concurrent Mark
    Heap 영역에서 전체 Region을 스캔하며 GC 대상 객체가 발견되지 않은 Region은 다음 단계에서 제외되도록 한다.
  • Remark
    스캔이 끝나면 GC 대상에서 제외될 객체를 식별한다. 이때 Stop The World가 발생한다.
  • Clean up
    살아있는 객체가 가장 적은 Region 부터 사용되지 않은 객체를 제거한다. 이때 Stop The World가 발생한다.
    완전히 비어진 Region은 재사용 가능한 형태로 동작한다.
  • Copy
    GC 대상이였지만 완전히 비워지지 않은 Region의 살아남은 객체들은 새로운 Region에 복사하여 Compaction 과정을 수행한다. 이때 Stop The World가 발생한다.

Z GC

JDK11부터 실험적으로 도입된 GC로 메모리 구조를 여러 사이즈의 Z Page 영역으로 나눠 메모리를 관리하는 방식이다.

Z GC 특징

  • Stop The World 시간이 최대 10초를 넘지 않고 Heap의 크기가 증가한다해도 Stop The World 시간이 크게 늘어나지 않기 때문에 큰 Heap 메모리에 적합한 방식이다.

Z GC 구조
Z GC는 Heap 영역을 Z Page 영역으로 나누고 다양한 사이즈로 할당된다.

factorio thumbnail

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

profile
내가 설명할 수 있어야 비로소 내가 아는 것이다

0개의 댓글