SY 개발일지
article thumbnail

가비지 컬렉션이란

가비지 컬렉션이란 메모리 관리 기법 중 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중 더 이상 필요없게 된 영역을 해제하는 기능입니다. 

 

여기서 더 이상 필요없게 된 영역이란, 아래 그림과 같이 재할당 등의 사유로 어떤 변수도 가리키지 않게 된 영역을 의미합니다.

 

 

자바에서의 가비지 컬렉션

가비지 컬렉션이란, 자바의 메모리 관리 방법 중 하나로, JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요없게 된 메모리 객체(garbage)를 모아 주기적으로 제거하는 프로세스를 말합니다.

자바에서는 가비지 컬렉터가 메모리 관리를 대신 해주기 때문에 Java 프로세스가 한정된 메모리를 효율적으로 사용할 수 있게 해주고, 개발자 입장에서 메모리 관리, 메모리 누수 문제에 대해 직접적으로 관리하지 않고 개발에만 집중할 수 있다는 장점이 있습니다.

하지만, 메모리가 언제 해제되는지 정확히 알 수 없어 이를 제어하기 어려우며, 가비지 컬렉션이 동작하는 동안에는 다른 동작도 멈추어 오버헤드가 발생한다는 문제점도 있습니다.

 

STW(Stop the world)
GC를 수행하기 위해 JVM이 프로그램 실행을 멈추는 현상. GC가 동작하는 동안에는 GC 관련 쓰레드를 제외한 모든 쓰레드가 동작을 멈추어 서비스 이용에 차질이 생길 수 있습니다.

 

물론 개발자가 직접 System.gc() 명령어를 통해 가비지 컬렉션을 호출할 수도 있지만, 해당 메서드는 호출하는 것만으로도 성능에 큰 영향을 미치므로 권하지 않습니다.

 

Mark And Sweep

가비지 컬렉션은 객체를 도달할 수 있는 객체(Reachable)와 도달할 수 없는 객체(Unreachable)로 나누어 객체가 참조되고 있지 않은 상태를 GC의 대상으로 합니다. Mark-Sweep 이란 다양한 GC에서 사용되고 있는 가비지 컬렉션이 동작하는 청소 과정에 대한 알고리즘입니다.

  • Mark: 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업
  • Sweep: Mark 단계에서 사용되지 않는다고 식별된 메모리를 해제하는 작업

Stop the world를 통해 모든 작업이 중단된 후, GC는 스택의 모든 변수 또는 Reachable 객체를 스캔하며 각각이 어떤 객체를 참조하고 있는지 탐색합니다. 그리고 사용되고 있는 메모리를 식별하는데, 이러한 과정을 Mark라고 합니다

이후, Mark 되지 않은 객체들을 메모리에서 제거하는데, 이러한 과정을 Sweep 이라고 합니다.

 

 

Mark and Sweep 방식은 루트로부터 해당 객체에 접근 가능한지가 해제의 기준이 됩니다.

JVM GC에서 Root Space는 Heap 메모리 영역을 참조하는 method area, static 변수, static, native method stack이 됩니다.

https://www.javacodegeeks.com/2015/05/what-is-garbage-collection.html

 

 

Heap 메모리 영역

JVM의 Heap 영역은 처음 설계될 때 다음 2가지 상태를 전재로 설계되었습니다.

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

아래 그래프를 보면 위 전제를 확인할 수 있습니다. 즉, 객체는 대부분 일회성이며, 메모리에 오랫동안 남아있는 경우는 드물다는 것입니다.

이러한 특성을 이용하여 JVM 개발자들은 효율적인 메모리 관리를 위해 객체의 생존 기간에 따라 물리적인 Heap 영역을 나누어 Young과 Old 총 2가지 영역으로 설계하였습니다.

 

Young 영역(Young Generation)

  • 새롭게 생성된 객체가 할당되는 영역
  • 대부분의 객체가 금방 unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라진다.
  • Young 영역에 대한 가비지 컬렉션을 Minor GC라고 합니다.

Old 영역(Old Generation)

  • Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역
  • Young 영역에서 보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생합니다.
  • Old 영역에 대한 가비지 컬렉션을 Major GC 또는 Full GC라고 합니다.

 

여기에서 효율적인 GC 동작을 위해, Young 영역을 3가지 영역(Eden, survival 0, survival 1)로 나누었습니다.

Eden 영역

  • new 를 통해 새로 생성된 객체가 위치합니다.
  • 정기적인 쓰레기 수집 후 살아남은 객체들을 Survival 영역으로 보냅니다.

Survivor  0/1 영역

  • 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역입니다.
  • Survivor 영역에는 특별한 규칙이 있는데, Survivor 0 또는 Survivor 1 둘 중 하나는 꼭 비어있어야 한다는 것입니다.

Old 영역이 Young 영역보다 크게 할당되는 이유는, Young 영역의 수명이 짧은 객체들은 큰 공간을 필요로 하지 않으며 큰 객체들은 Young 영역이 아니라 바로 Old 영역에 할당되기 때문입니다.

 

동작원리

Minor GC

1. 처음 생성된 객체는 Young Generation 내 Eden 영역에 생성됩니다.

2. 객체가 계속 생성되어 Eden 영역이 꽉 차게 되면 Minor GC가 발생합니다.

3. Mark 동작을 통해 reachable 객체를 탐색합니다.

4. Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동하게 됩니다.

5. Eden 영역에서 사용되지 않는 Unreachable 객체는 메모리에서 해제됩니다.(Sweep)

6. 살아남은 모든 객체는 age 값이 1씩 증가됩니다.

age 값이란 ?
Survivor 영역에서 객체의 객체가 살아남은 횟수를 의미하는 값이며, Object Header에 기록됩니다.
만일 age값이 임계값에 다다르면 Promotion(Old 영역으로 이동)의 여부를 결정합니다.
JVM 중 가장 일반적인 HotSpot JVM의 경우 이 age의 기본 임계값은 31이라고 합니다. 그 이유는 객체 헤더에 age를 기록하는 부분이 6bit로 되어 있기 때문이라고 합니다.

또한 Survivor 영역의 제한 조건으로 Survivor 영역 중 반드시 1개는 사용되어야 하고, 나머지는 비어있어야 합니다.
만약 두 Survivor 영역에 모두 데이터가 존재하거나 모두 사용량이 0이라면 현재 시스템이 정상적인 상황이 아니라는 반증이 됩니다.

 

7. 또다시 Eden 영역이 꽉차게 되면 Minor GC가 발생하고 Mark 합니다.

8. Marking 한 객체들과 Survivor 0에 있던 객체들을 Survivor 1로 이동하고 Sweep 합니다.

9.살아남은 객체들은 또다시 age 값이 1씩 증가됩니다.

10. 이러한 과정을 반복합니다.

Major GC

1. Survivor 내의 객체의 age가 임계값(예시는 5)에 도달하게 되면

2. 해당 객체들을 Old Generation으로 이동시킵니다. 이를 Promotion이라고 합니다.

3. 위의 과정이 반복되어 Old Generation 영역의 메모리가 부족하게 되면 Major GC 가 발생하게 됩니다.

 

 Major GC는 Old 영역의 데이터가 가득차면 GC를 실행하는 방식입니다. 즉 Old 영역에 할당된 메모리가 허용치를 넘게 되면, Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 Major GC가 실행됩니다.

그러나 Old 영역은 Young 영역에 비해 상대적으로 큰 공간을 가지고 있어, 이 공간에서 메모리 상의 객체 제거에 많은 시간이 걸리게 됩니다. (약 10배 이상이 차이가 난다고 합니다)

 

 

참고

profile

SY 개발일지

@SY 키키

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!