[SW정글 138일차] Garbage Collection

rg.log·2023년 2월 3일
0

SW 사관학교 JUNGLE

목록 보기
30/31
post-thumbnail

며칠 전, Go개발자와 이야기를 나누다 Go의 Garbage Collection이 흥미롭다며 이야기를 꺼냈다.
Go의 Garbage Collection은 사용하지 않는 메모리(가비지)를 Garbage Collector를 통해 삭제한 후 해당 메모리 만큼을 가비지를 수집해야 그다음번 Garbage Collection이 작동된다고 한다. 그렇기 때문에 점점 느려져, c언어처럼 mmap계열을 사용해 커널의 메모리 API를 사용하는 등으로 메모리 오버헤드를 제거한다고 한다. PintOS 생각난다며 신나있던 그의 모습을 잊을 수가 없다.. 천생 개발자..

Go를 사용해보지 않아서 잘 모르지만, 'Java는? Java의 Garbage Collection은 어떻게 동작할까?' 하는 호기심이 생겼다.

Java Garbage Collection

자바의 메모리 관리 방법 중 하나로 JVM의 Heap 영역에서 동적으로 할당했던 메모리 영역 중 필요 없게 된 메모리 영역을 주기적으로 삭제하는 프로세스를 말한다.

PintOS 당시 C에서는 이러한 가비지 컬렉션이 없어 수동으로 메모리 할당과 해제를 일일이 해줘야 했지만, Java는 JVM에 탑재된 가비지 컬렉터가 메모리 관리를 대행해줘서 개발자 입장에서는 메모리 관리, 메모리 누수 문제에 대해 완벽히 관리하지 않고 오롯이 개발에만 집중할 수 있다는 장점이 있다.
red-black tree를 C언어로 구현할 때도 할당한 메모리를 해제해주지 않는다면, test case를 돌렸을 때 누수된 메모리가 상당했고, 예기치 못하게 종료됬던 것이 기억에 남는다.

반면, 가비지 컬렉션도 단점이 존재하는데
1. 개발자가 메모리가 언제 해제되는지 정확히 알 수 없다.
2. 가비지 컬랙션이 동작하는 동안에 다른 동작을 멈추기 때문에 오버헤드가 발생한다.
이로 인해 가비지 컬랙터가 너무 자주 실행되면 소프트웨어 성능 하락의 문제가 되기도 한다.
이러한 특성으로 실시간으로 계속 동작해줘야하는 시스템들은 잠깐의 소프트웨어 일시정도로 목표한 결과가 달라질 수 있어 가비지 컬렉션의 사용이 적합하지 않는다.

따라서, 가비지 컬렉션. 줄여서 GC를 수행하기 위해 JVM이 GC 관련 Thread를 제외한 모든 Thread는 멈추는 현상인 Stop The World를 최소화 시키는 것이 우리의 일이다. 어플리케이션의 사용성과 GC의 편리함의 장점은 유지하면서 효율적이게 GC를 실행하는 최적화하는 것!

가비지 컬렉터는 어떤 Object를 Garbage로 판단할까?

JVM 메모리는 객체들이 Heap에 생성되고, Method Area나 Stack에서는 그 객체의 주소만 참조하는 형식이다. 객체를 참조하고 있다면 reachable로 구분되고, 그렇지 않다면 unreachable로 구분하고 수거해버린다.


이는 메서드가 끝나는 등의 이벤트로 인해 Heap영역의 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제되면, 위 그림에서 하늘색 객체와 같이 어디서도 참조하고 있지 않은 객체가 발생하게 된다. 이를 쓰레기로 간주하고 가비지 컬렉터가 제거해주는 것이다.

Garbage로 판단한 객체를 어떤 방식으로 제거할까?

Mark And Sweep

  1. Mark
    Root로부터 그래프를 순회하듯 연결된 객체들을 찾아 어떤 객체를 참조하고 있는지 마킹한다.
  2. Sweep
    참조하고 있지 않은 객체들을 Heap에서 제거한다.
  3. Compact (가비지 컬렉터 종류에 따라 하지 않는 경우도 있음)
    제거 후 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 아닌 부분으로 압축한다.

JVM의 Heap을 자세히 보자.

weak generational hypothesis

아래는 객체의 수명에 대한 일반적인 분포를 나타낸 그래프이다. 대부분의 객체는 "어린 나이에 죽는다"는 사실에 초점을 맞추면 효율적으로 Heap을 관리할 수 있다.

이러한 특성을 활용해 JVM 개발자들은 보다 효율적 메모리 관리를 위해, 물리적인 Heap영역을 Young과 Old로 나누어 설계하였다.

  • Young Generation 영역
    새롭게 생성한 객체의 대부분이 위치한다.
    대부분의 객체가 금방 사라지는 unreachable 상태가 된다.
    Young 영역에 대한 가비지 컬렉션을 Minor GC라고 한다.

  • Old Generation 영역
    Young Generation에서 오래 살아남은 객체들이 복사되는 영역이다.
    Young 영역보다 크게 할당되며, 가비지는 적게 발생한다.
    Young 영역에 대한 가비지 컬렉션을 Major GC라고 한다.

더 효율적인 GC를 위해 Young 영역을 3가지로 나누면, Eden, survivor 0, survivor 1로 구성된다.

  • Eden
    new를 통해 새로 생성된 객체가 위치한다.
    정기적인 쓰레기 수집 후 살아남은 객체들은 survivor 0으로 보낸다.

  • survivor 0 / survivor 1
    최소 1번의 GC 이상 살아남은 객체가 존재하는 영역으로,
    둘 중 하나는 꼭 비어있어야 한다.


가비지 컬렉터가 실행되는 상황은 언제일까?

Minor GC 과정

처음 생성된 객체는 Young 영역 일부인 Eden 영역에 위치하게 된다.
객체가 계속 생성되 Eden 영역이 꽉차면 Minor GC 가 실행된다.

Major GC 과정

Major GC는 객체들이 계속 늘어나 Old 영역의 메모리가 부족하면 발생한다.
Old 영역은 상대적으로 큰 공간을 가지고 있어, 메모리 상의 객체를 제거하는데 많은 시간이 걸린다. (Young 영역의 10배이상)
여기서 Stop-The-World 문제가 발생한다!

하여 자바 개발자들은 끊임없이 가비지 컬렉션 알고리즘을 발전 시켜왔다.
Serial GC, Parallel GC, Parallel Old GC, CMS GC, G1 GC, Shenandoah GC, ZGC


참고.
HotSpot Virtual Machine Garbage Collection Tuning Guide
Java Garbage Collection
Java ☕ 가비지 컬렉션 동작 원리 & GC 종류 💯 총정리

추가로 Go의 가비지 컬렉터가 궁금하다면

0개의 댓글