2 분 소요

GC

Java는 명시적으로 메모리를 건드리지 않는다. Garbage Collection이라는 알고리즘으로 관리하며 개발자가 메모리를 관리할 필요가 없도록 한다.

1. GC와 Heap

JVM Runtime Area의 영역들은 쓰레드가 시작할 때 생성되는 경우가 있는가 하면, JVM이 시작할 때 생기는 영역이 있다. 후자가 바로 Heap이다.

Heap은 new 키워드로 생성된 인스턴스 및 배열이 저장되는 곳으로 모든 쓰레드가 공유한다. 그리고 객체의 생성이 빈번한 java 특성상 메모리 이슈가 이곳에서 발생하기 Garbage Collection은 Heap에서 일어난다.

Heap은 크게 Young 영역Old 영역으로 구성된다. 그리고 Young 영역은 하나의 Eden 영역두 개의 Survivor 영역으로 구성된다 즉 4개의 영역으로 JVM의 Heap이 구성된다고 볼 수 있다.

2. GC의 전제조건과 시나리오

Java의 Garbage Collection은 메모리 관리를 알아서 해준다고 하는데 전제조건 하에 최적화되어 동작하도록 만들어졌다.

  • 대부분 객체는 금방 참조가 끊김
  • 오래된 객체에서 젊은 객체로의 참조는 많지 않음

먼저 객체가 new 키워드로 생성될 때 Eden 영역에 생성된다.

그리고 Eden 영역이 꽉 차게 되면 Eden 영역에서 GC가 한번 일어나게 되며 참조가 끊긴 객체는 지워지고 살아남은 객체는 Survivor 영역 중 하나로 이동한다.

위의 동작이 반복 돼가 하나의 Survivor 영역이 가득 차게 되면 그 Survivor영역에서 GC가 일어난다. 그리고 여기서 살아남은 객체들은 나머지 Survivor 영역으로 이동한다 이때 GC가 일어난 Survivor 영역은 비어있어야 한다.

Survivor 1과 2를 왔다 갔다하는 과정이 반복되며 계속 살아있는 객체는 Old영역으로 이동한다. Eden영역의 살아있는 객체가 Survivor영역보다 더 큰 경우 바로 Old로 옮겨진다.

3. GC 방식

GC 방식 전에 알아야 할 개념은 stop-the-world 이다. GC를 실행하기 위해서 JVM이 모든 애플리케이션 실행을 멈추는 것이다. stop-the-world가 발생하면 GC를 실행하는 쓰레드를 제외한 나머지 스레드는 작업을 멈춘다.

모든 GC 방식에서 stop-the-world가 발생한다. 이런 stop-the-world를 줄이는 것이 GC 튜닝이라고 할 수 있다.

GC 방식은 총 5가지가 있다.

  • 시리얼콜렉터
  • 병렬 콜렉터
  • 병렬 압축 콜렉터
  • CMS 콜렉터
  • G1 콜렉터

1. 시리얼콜렉터

Mark-sweep-compact 알고리즘을 따른다. 살아있는 객체를 식별하고 Old영역의 가장 앞부분부터 살아 있는 것만 남기고 삭제하며 마지막으로 살아있는 객체들을 가장 앞쪽으로 모아준다.

이 방식은 굉장히 느리다. 그렇지만 그만큼 시스템 자원을 덜 사용한다. 속도와 별 상관없고 시스템 자원이 부족할 때 사용하는 GC 방식이다.

2. 병렬 콜렉터

시리얼 콜렉터 방식과 GC 알고리즘은 같다. 하지만 단일 스레드가 GC를 수행하는 시리얼 콜렉터 방식과 달리 병렬 콜렉터 방식은 GC를 처리하는 쓰레드가 여러개이다. 당연히 시스템 자원이 넉넉할 때 유리한 방식이다.

3. 병렬 압축 콜렉터

Mark-Summary-compaction 알고리즘을 사용한다.

Sweep은 단일 스레드가 Old영역 전체를 훑어 살아있는 객체만 찾아내는 방식이지만 Summary는 여러 쓰레드가 Old 영역을 분리하여 훑는다. 효율을 위해서 앞선 GC에서 Compaction된 영역을 별도로 훑는다.

4. CMS 콜렉터

Young 영역 GC 방식은 병렬 콜렉터와 같다. 다른 점은 Old영역에서는 다음과 같은 단계를 거친다.

  • Inital Mark
  • Concurrent Mark
  • Remark
  • Concurrent Sweep

Initial Mark 단계는 클래스 로더에서 가장 가까운 객체 중에서만 살아있는 객체를 찾는다. 따라서 멈추는 시간이 매우 짧다.

Concurrent Mark단계는 위 단계에서 살아남은 객체의 참조를 따라가며 살아있는 객체를 찾는다. 이때 여러 개의 쓰레드가 동작한다.

Remark 단계에서는 Concurrent Mark를 수행하는 동안 객체의 참조가 끊기거나 새롭게 생긴 객체가 없는지 다시 한번 확인한다.

Concurrent Sweep 단계에서는 쓰레기를 정리한다. 별도의 Compaction이 없음에 유의한다.

장점은 stop-the-world 시간이 매우 짧다는 것이다. 하지만 반대로 시스템 자원을 더 많이 사용하고 Compaction 단계가 없어 Old 영역의 크기가 충분하지 않거나 크기에 비해 조각난 메모리가 많은 경우 오히려 stop-the-world시간이 늘어날 수 있다.

5. G1 콜렉터

Java7에서 새롭게 소개된 GC 방식이다. 바둑판 모양의 영역이 각각 Eden, Survivor, Old 영역의 역할을 동적으로 바꿔가며 GC가 일어난다. Young 영역의 GC와 Old 영역의 GC는 모두 CMS 콜렉터 방식과 유사하다.

참고: https://preamtree.tistory.com/118

업데이트: