가비지 컬렉터(GC)를 알아보자

henrywoo·2022년 5월 11일
8
post-thumbnail

유효하지 않은 메모리(Garbage)를 자동으로 제거해주는 작업이다.
Java Application은 JVM 위에서 구동되는데, JVM의 기능 중 하나이다.
더 이상 사용하지 않는 객체를 청소하여 메모리 공간을 확보할 수 있다.

GC가 왜 필요할까?

Heap 영역에 객체들이 계속 쌓이면 OutOfMemoryException이 발생한다.
이를 방지하기 위해 주기적으로 사용하지 않는 객체를 수집하여 제거해준다.

public class Main {
    public static void main(String[] args) {
        Person person = new Person("a", "곧 참조되지 않음");
        person = new Person("b", "참조가 유지됨");
        //GC 발생 가정 시점
    }
}

//Person은 "name"과 "description"을 필드로 가지며, 생성자에서 차례대로 주입받게 됨.

이처럼 GC가 주로 동작하는 대상은 Heap 영역 내의 객체 중에서 참조되지 않은 데이터다.
위 코드에서 b는 참조가 유지된 상태이기 때문에 GC의 대상이 아니지만, a는 GC의 대상이 된다.
위의 경우처럼 참조하는 대상을 바꾸거나 메서드 실행이 끝나서 Stack이 pop되면 참조되지 않는 객체들이 생긴다.

Reachability

참조되고 있는지에 대한 개념을 Reachability 라고 한다.
유효한 참조를 Reachable, 참조되지 않으면 Unreachable 이라고 한다.
GC는 바로 Unreachable한 객체들을 garbage라고 인식하게 된다.

위 그림처럼, Heap 영역 내부의 객체들은 Method Area , Stack , Native Stack 에서 참조되면 reachable 로 판정된다. (메서드 등에서 사용!)

(이렇게 reachable 로 인식되게 만들어주는 JVM Runtime Area 들을 root set 이라고 한다.)

🔥 reachable 이 참조하는 다른 객체들 역시 reachable 이 된다.
여기서 root set에 의해 참조되고 있지 않은 객체들은 unreachable 로 판정되어, GC의 대상이 된다.

가비지 컬렉션 용어 정리

  • Stop the world : 가비지 컬렉션을 수행하기 위해 JVM이 애플리케이션을 일시정지하는 것. 가비지 컬렉션이 실행되면 GC 작업을 맡은 스레드 이외의 스레드들은 모두 멈추고 GC 작업이 종료되면 재개한다. 너무 빈번한 GC 실행은 성능을 저하시킨다. (GC의 성능 개선을 위해 튜닝한다고 하면 보통 Stop the world 시간을 줄이는 작업을 말한다)
  • Mark : 애플리케이션이 일시 중지되면 GC가 참조된 객체와 연결된 객체들을 타고 이동하며 접근 가능한 객체를 식별하는 과정이다.
  • Sweep : 모든 객체 탐색이 끝나면 식별(Mark)되지 않은 객체들을 메모리에서 해제시키는 과정이다.
  • Young : 새롭게 생성된 객체가 할당되는 영역이다. Young 영역에 대한 가비지 컬렉션을 Minor GC라고 부른다. 대부분 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라진다.
  • Old : Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역, Old 영역은 Young 영역보다 크게 할당되며, 크기가 큰 만큼 가비지는 적게 발생한다. 이 영역이 가득 차면 Major GC가 발생한다.
  • Eden : 새로 생성된 객체가 할당되는 영역이다. Eden 영역이 꽉 차면 GC가 발생하면서 Mark , Sweep 과정이 일어난다. 아직 사용중인 객체는 Survivor 영역으로 이동하며, Eden 영역은 비워진다.
  • Survivor : Eden 영역이 꽉 차게 되면, GC가 발생하면서 제거된 객체 외의 나머지 살아남은 객체는 다른 Survivor 영역으로 이동하게 된다.

가비지 컬렉션 알고리즘

GC를 성공적으로 수행하는 알고리즘을 설계하기 위해서는 몇 가지 가설이 필요하다.
그 중 대표적인 것이 Weak Generational Hypothesis 이다.
이 가설은 대부분 객체는 빠르게 unreachable한 상태로 전환된다 고 본다.
또한, Heap 메모리 영역 기준으로 오래된 영역에서 최신 영역으로의 참조 방향은 적게 존재한다고 가정한다.

아래는 이 가설을 바탕으로 한 가장 기본적인 알고리즘들이다.

Mark And Sweep Algorithm

가장 기본적인 GC 알고리즘이다.

  • Mark Phase 단계 : root set 으로부터 출발하여, 참조되는 객체들에 대해서 Mark(접근 가능한 객체 식별)한다.
  • Sweep Phase 단계 : Mark Phase 단계 이후에 Mark 되지 않은 객체들을 추적하여 삭제한다.

위와 같이 메모리를 해제하는 것을 알 수 있다.
하지만 이 알고리즘은 메모리가 Fragmentation(단편화) 되는 단점이 있다.
(메모리에서의 단편화정렬되지 않은 조각으로 나뉘어져, 절대적인 크기는 충분함에도 추가적으로 메모리 할당이 불가한 상태를 말한다.)
이러한 단점을 해결하기 위해 Mark And Compact Algorithm 이 탄생했다.

Mark And Compact Algorithm

이 알고리즘은 Mark And Sweep Algorithm 처럼 참조되는 객체들에 대해 Mark하고, 참조되지 않으면 삭제한다. 삭제한 이후, 메모리를 정리하여 메모리 단편화를 해결한다!!!

많은 GC 방식들이 이 Algorithm을 바탕으로 구현된다.

Major GC, Minor GC

GC는 일어나는 시점에 따라 Major GCMinor GC 로 나눌 수 있다.
Minor GC 는 JVM의 Young 영역에서 일어나는 GC이다.
만약 Young 영역이 가득 차게 되어 더 이상 새로운 객체를 생성할 수 없을 때 Minor GC 가 일어난다.
이는 Mark된 영역이 다음 영역으로 복사되면서 진행된다.

이 때, Mark 된 영역만 복사되기 때문에 삭제 과정은 이루어지지 않는다.
이는 Major GC에 비해서 상대적으로 시간이 짧아서, Stop-The-World 가 이루어지지 않는다고 알려진다.

실제로는 Stop-The-World가 이루어지며, GC 를 담당하는 쓰레드를 제외하고 모든 쓰레드가 멈추게 된다. 하지만 이 시간이 무시가 가능할 정도로 짧게 이루어지기 때문에 Stop-The-World 가 이루어지지 않는다고 간주하곤 한다.

이와 다르게 Major GCOld영역에서 이루어진다.
상당히 긴 시간 Stop-The-World가 이루어진다.
이를 해결하기 위해 Major GC에서는 우리가 앞에서 다룬 Mark And Compact Algorithm을 기반으로 한 여러 GC 방식들이 선택 및 적용된다.

마무리

JVM의 메모리 영역, 그 중에서도 Heap 메모리를 정리해주는 GC의 기초에 대해 살펴봤다.
실제로 Serial GC , Parallel GC 등 GC 방식을 직접 선택할 수 있다.
각자의 Java 프로그램에 맞는 GC 방식을 채택할 수 있는 개발자가 되자!!


레퍼런스

https://github.com/NKLCWDT/cs/blob/main/Java/GarbageCollection.md
https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/

profile
가보자 가보자

0개의 댓글