Java 의 Garbage Collection

박지찬·2023년 9월 26일

이번 글은 JVM 의 핵심 구성 요소 중 하나인 Garbage Collection (GC) 에 관한 내용입니다. Java 는 C/C++ 처럼 메모리 관리를 직접 해줄 필요가 없단 장점이 있는데, 이 GC 가 어떻게 동작하는지 알아보자.

Garbage Collection 이란?

자바의 Garbage Collection (GC) 는 더 이상 사용되지 않는 데이터가 할당되어 있는 메모리를 해제시켜주는 장치이다. JVM 에서 자동으로 동작하기 떄문에 C/C++ 처럼 코드 상에서 직접 명시해줄 필요가 없다. GC가 주로 동작하는 대상은 Heap 영역 내에서 참조되지 않는 데이터이다.

예를 들면

public class Main {
	public static void main(String[] args) {
    	Foo foo = new Foo("a");
        foo = new Foo("b");
        //new Foo("a") 은 더 이상 참조되지 않음
    }
}

위 코드에서 foo는 처음에 a라는 값을 가진 객체를 참조한다. 하지만 곧 b를 값으로 가진 객체를 참조하게 되어 a를 값으로 가진 객체는 더 이상 참조가 되지 않는다.

이렇게 더 이상 참조되지 않는 데이터를 unreachable 이라고 하는데, 이러한 unreachable 한 데이터들을 GC 는 garbage 라고 인식한다.

[이미지 출처: Java Reference와 GC]

힙에 있는 객체들은 다음 4 가지 종류 중 하나로 참조된다

  • 힙 내 다른 객체에 의한 참조
  • Java Stack, 즉 Java 메서드 실행 시에 사용하는 지역 변수와 파라미터에 의한 참조
  • 네이티브 스택, JNI (Java Native Interface)에 의해 생성된 객체에 의한 참조
  • 메서드 영역 정적 변수에 의한 참조

이 4 가지 중 힙 내의 다른 객체에 의한 참조를 제외한 나머지 3개가 root set으로, reachability 를 판가름하는 기준이 되고, reachable 한 객체가 참조하는 객체도 reachable 이 된다.

하지만 root set 에 의해 참조되지 않는 객체들은 unreachable 하다고 판단이 되고 GC 의 대상이 된다.

Stop-The-World

GC 실행 방식을 알아보기 전 알아야 할 개념이 바로 Stop-The-World 이다. JVM 은 GC 를 통해 unreachable 한 객체를 정리하여 메모리를 확보하는데, 그렇다면 GC 를 자주 실행시키면, 여유 메모리를 최대한 확보하여 성능이 좋아진다고 생각할 수도 있다. 하지만 GC 가 일어나면 GC 를 담당하는 쓰레드를 제외한 모든 쓰레드의 작동이 일시적으로 정지된다. 이를 Stop-The-World 현상이라고 한다. 그래서 흔히 GC 튜닝이란 Stop-The-World 의 시간을 줄이는 것을 말한다.

GC 의 동작 방식

Weak Generational Hypothesis

가비지 컬렉터는 두 가지 가설 하에 만들어졌다.

  • 대부분의 객체는 금방 접근 불가능 상태 (unreachable state) 가 된다
  • 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다

이 두가지 가설 하에 GC의 아키텍쳐와 동작 방식이 설계되었다.

이 가설의 장점을 최대한 살리기 위해서 크게 Young 영역 과 Old 영역으로 물리적 공간을 나눈다

  • Young Generation 영역 : 새롭게 생성한 객체 대부분이 여기에 위치한다. 대부분의 객체는 금방 접근 불가능 상태가 되기 때문에 많은 객체가 Young 영역에서 생성되었다가 사라진다. 이 영역에서 객체가 사라질 때 Minor GC 발생한다
  • Old Generation 영역 : 접근 불가능 상태로 되지 않아 Young 영역에서 살아남은 객체가 여기로 복사된다. Young 영역보다 GC는 적게 발생한다. 이 영역에서 객체가 사라질 때 Major GC (혹은 Full GC)가 발생한다

Mark And Sweep Algorithm

[이미지 출처: JVM에 관하여 - Part 4, Garbage Collection 기초]

  • Mark Phase: 참조되는 객체들을 마크한다
  • Sweep Phase: 마크되지 않은 객체들을 추적하여 삭제한다

이러한 방식은 메모리에 Fragmentation 이 발생한다. 그래서 이를 보완하기 위해 Mark And Compact Algorithm 을 사용한다.

Mark And Compact Algorithm

[이미지 출처: JVM에 관하여 - Part 4, Garbage Collection 기초]

참조되지 않은 객체를 삭제한 후에, 메모리를 정리하여 메모리 단편화를 해결한다. 많은 GC 방식들이 이 알고리즘을 바탕으로 구현되어 있다.

Minor GC

[이미지 출처: JVM에 관하여 - Part 4, Garbage Collection 기초]

먼저 Minor GC 는 Young 영역에서 일어나는 GC이다. Young 에 위치한 각각의 영역이 가득 차게 되어 더 이상 새로운 객체를 생성할 수 없을 때 발생한다. 마크된 영역이 다음 영역으로 복사가 되면서 이루어진다.

HotSpot VM 에서 Young 영역은 3 영역으로 나뉜다

  • Eden 영역
  • 2개의 Survivor 영역

그리고 이 3 영역은 다음과 같은 방식으로 동작한다 :

  • 새로 생성한 객체는 대부분 Eden 영역에 들어간다
  • Eden 영역에서 가득 차면 GC 가 발생한다. 이 때 마크된 객체들이 Survivor 영역 중 하나로 들어가게 된다
  • Survivor 영역 둘 중 하나는 비어있게 된다
  • 하나의 Survivor 영역이 가득차면 그 중에서 살아남은 객체가 다른 Survivor 영역으로 이동한다
  • 이 과정을 반복하다 threshold 에 도달하면 살아남은 객체는 Old 영역에 들어가도록 promote 된다

Card Table

Old 영역에서 Young 영역의 어떤 객체를 참조하는지 알기 위해 Old 영역에 Card Table 을 저장한다. Minor GC 발생 시 Old 영역이 어떤 객체를 참조하는지 하나하나 확인하는 것이 아닌 이 Card Table 을 참조한다.

Minor GC 에서의 Stop-The-World

추가로 알아두면 좋을 것은, Minor GC 가 발생할 때 Stop-The-World 가 발생하지 않는다고 많이 알려져 있지만, 사실 Minor GC 에서도 발생한다. 하지만 Major GC 와 비교해서 매우 짧은 시간 동안 발생한다.

HotSpot VM 에서의 Minor GC

bump-the-pointer

HotSpot VM 은 Minor GC 를 빠르게 하기 위해 두 가지 기술을 사용한다. 그 중 한 가지가 바로 bump-the-pointer 이다. Young 영역에 있는 Eden 과 Survivor 영역의 최상단을 추적하는 포인터를 둬서 새로운 객체를 생성 할 때 이 포인터를 통해 남은 공간이 충분한지 점검할 수 있으므로 빠르게 메모리 할당이 이루어진다.

Thread-Local Allocation Buffers (TLABs)

멀티 스레드 환경에서 Thread-Safe 하게 여러 스레드에서 사용하는 객체를 Eden 영역에 저장하려면 락이 발생하여 성능이 저하된다. 이 문제를 해결하기 위해 HotSpot VM 에서는 TLABs 를 사용한다.

각각의 스레드가 각각의 몫에 해당하는 Eden 영역의 작은 덩어리를 가진다. 각 쓰레드는 자기가 갖고 있는 TLAB 에만 접근할 수 있기 때문에, bump-the-pointer 를 사용해도 락이 없이 메모리 할당이 가능하다.

Major GC

Old 영역이 가득 차면 발생한다. 이 과정에서 앞서 설명했던 Mark-And-Compact 방식이 대표적으로 사용된다.

Major GC 에서의 Stop-The-World 시간은 길다

Full GC

Heap 영역이 모두 가득 차면 발생하며, Young 영역과 Old 영역을 모두 정리한다.

참고자료

https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/
https://d2.naver.com/helloworld/1329
https://d2.naver.com/helloworld/329631
https://www.perfmatrix.com/how-does-garbage-collector-work/
https://plumbr.io/blog/garbage-collection/minor-gc-vs-major-gc-vs-full-gc

0개의 댓글