본문 바로가기
JAVA

[JAVA] Garbage Collection의 개념과 동작 원리

by 무사뎀벨레 2022. 3. 28.

 

 

 

Garbage Collection자동으로 사용되지 않는 메모리를 정리해주는 기능입니다.

자바에는 Garbage Collection이라고 하는 메모리 관리 기능이 존재합니다.

 

 

 

 

 

 

Garbage Collection 이란?


쓰레기를 수집한다는 이름에서 알 수 있듯이 프로그래머가 동적으로 할당한 메모리 영역 중 더 이상 사용하지 않은 자원을 찾아내어 해제하는 기능입니다. 앞글자를 따서 GC라고도 부르는데, 그 시작은 1959년에 존 매카시라는 인물이 LISP의 메모리 관리를 위해 처음 만들었다고 합니다.

LISP란?
1958년 존 매카시가 개발하였으며, 대표적인 함수형 언어 중 하나이다.

 

 

 

 

 

 

 

등장 배경


옛날의 언어들은 동적인 메모리 할당 기능이 아예 없거나, 프로그래머가 할당한 뒤 수동으로 해제까지 하는 방식이 존재했습니다. 

 

그럼에도 불구하고 메모리를 할당해놓고 필요없어진 뒤에도 해제를 안 해서 메모리 누수가 생기거나, 혹은 거꾸로 해제했던 메모리를 실수로 다시 사용하거나, 해제했던 메모리를 또 해제한다거나 하는 실수들로 인해 많은 버그들이 생기곤 했습니다.

 

게다가 일반적으로 버그는 재현이 가능하고 오류가 있는 부분으로부터 가까운곳에서 발생해야 파악하기 쉬운데, 메모리 관련 버그는 한참 떨어진 곳에서 발생하고 재현이 어려운 경우가 대부분이었습니다.

 

그렇기에 이러한 문제들은 해결하기 위해서 제시된것이 Garbage Collection입니다. 보통 쓰레기 수집 기능을 채택한 언어의 경우 프로그래머에게 직접적인 메모리 할당과 해제를 하게 하는 대신 쓰레기 수집기에서 제공하는 할당과 해제를 사용하게 하여 프로그램이 실행되는 중간에 쓸모없는 메모리, 즉 쓰레기를 알아서 수집하게 됩니다.

 

 

 

 

 

 

 

 

JAVA에서의 Garbage Collection


JVM(Java Virtual Machine)에서는 메모리를 자동으로 관리해주는 Garbage Collector(가비지 콜렉터)가 존재합니다.

정확히는 JVM 메모리 중 Heap 영역에서 사용하지 않는 객체를 삭제해 줍니다.

 

이미지출처 https://namu.wiki/w/%EC%9E%90%EB%B0%94%20%EA%B0%80%EC%83%81%20%EB%A8%B8%EC%8B%A0?from=JVM

위 이미지는 JVM의 메모리 영역을 보여주고 있습니다. Method Area, Heap, JVM Language Stacks, PC Resgisters, Native Method Area 총 5개의 영역으로 구성되어있습니다.

 

그중 Heap메모리는 아래와 같은 특징을 가지고 있습니다.

1. new를 통하여 동적으로 생성되는 객체가 저장되는 공간(또는 배열)

2. Heap에 저장된 데이터는 메모리 관리가 필요한 GC 대상

 - 만약 참조하지 않는 데이터라면 GC에게 제거됨

3. 모든 쓰레드(Thread)가 공유하는 공간

4. JVM이 실행될때 생성됨

 

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

1. 대부분의 객체는 금방 접근 불가한 상태가 된다.

2. 오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.

 

즉, 객체는 대부분 일회성되며, 메모리에 오랫동안 남아있는 경우는 드물다 라는 것입니다. 그렇기 때문에 객체의 생존기간에 따라 물리적인 Heap 영역을 나누게 되었고 Young, Old 총 2가지 영역으로 설계되었습니다. 초기에는 Perm영역이 존재했지만 Java8부터 제거되었습니다.

출처 https://jackyfkc.github.io/cs/language/java/gc.html

위 이미지는 Heap 영역을 보여줍니다.

 

Young Generation(Young 영역)

- 새롭게 생성된 객체가 할당 되는 영역

- 대부분의 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다 사라짐

- Young 영역에 대한 가비지 컬렉션Minor GC라고 부름

eden
- Young 영역 중에서도 특히 막 생성된 객체들이 위치하는 곳
survivor
- 영역이 두개 존재하는데 eden에서 생존된 객체들이 당분간 생존해 있는 곳
Reachable 
- 아직 어디에선가 사용하고 있는 객체
Unreachable
- 아무도 사용하지 않고 자리만 차지하고 있는 객체

Old Generation(Old 영역)

- Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역

- 특정 횟수 이상을 살아남은 reference가 살아있는 곳

- Young 영역보다 크게 할당되며, 영역의 크기가 쓴 만큼 가비지는 적게 발생

- Old 영역에 대한 가비지 컬렉션Major GC 또는 Full GC라고 부름

 

Permanent

- Method Area의 메타정보가 기록된 곳

 

 

 

 

 

 

 

 

Garbage Collection의 동작 방식


Young 영역과 Old 영역은 서로 다른 메모리 구조로 되어있기 때문에 세부적인 동작 방식이 다릅니다. 하지만 기본적으로 가비지 컬렉션이 실행된다고 하면 아래의 두 가지 공통적인 단계를 지나게 됩니다.

1. Stop The World

2. Mark And Sweep

 

Stop The World

Stop The World는 가비지 컬렉션을 실행하기 위해 JVM이 애플리케이션의 실행을 멈추는 작업을 말합니다. GC가 실행될 때는 GC를 실행하는 스레드를 제외한 모든 스레드들의 작업이 중단되고, GC가 완료되면 작업이 재개됩니다. 당연히 모든 스레드들의 작업이 중단되면 애플리케이션이 멈추기 때문에, GC의 성능 개선을 위해 튜닝을 한다고 하면 보통 Stop The World의 시간을 줄이는 작업을 하는 것입니다. 또한 JVM에서도 이러한 문제를 해결하기 위해 다양한 실행 옵션을 제공하고 있습니다.

 

Mark And Sweep

Mark
사용되는 메모리와 사용되지 않는 메모리를 식별되는 작업

Sweep
Mark 단계에서 사용되지 않음으로 식별된 메모리를 해제하는 작업

Stop The World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수 또는 Reachable 객체를 스캔하면서 각각 어떤 객체를 참조하고 있는지를 탐색하게 됩니다. 그리고 사용되고 있는 메모리를 식별하는데, 이러한 과정을 Mark라고 합니다. 이후에 Mark가 되지 않은 객체들을 메모리에서 제거하는데, 이러한 과정을 Sweep이라고 합니다.

 

 

출처 https://www.freecodecamp.org/news/garbage-collection-in-java-what-is-gc-and-how-it-works-in-the-jvm/

Minor GC의 동작 방식

Young 영역은 1개의 Eden영역2개의 Survivor 영역, 총 3가지로 나뉩니다. 객체가 새롭게 생성되면 Young 영역 중에서도 Eden 영역에 할당됩니다. 그리고 Eden 영역이 가득 차면 Minor GC가 발생하게 되는데, 사용되지 않는 메모리는 해제되고 Eden 영역에 존재하는 객체는 (사용 중인) Survivor 영역으로 옮겨지게 됩니다. Survivor 영역은 총 2개이지만 반드시 1개의 영역에만 데이터가 존재해야 하는데, Young 영역의 동작 순서는 아래와 같이 자세히 설명할 수 있습니다.

 

1. 새로 생성된 객체가 Eden 영역에 할당된다.

2. 객체가 계속 생성되어 Eden 영역이 가득 차게 되고 Minor GC가 실행된다.

    2-1. Eden 영역에서 사용되지 않는 객체의 메모리가 해제된다.

    2-2. Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동된다.

3. 1~2번 과정이 반복되다 Survivor 영역이 가득 차게 되면 Survivor 영역의 살아남은 객체를 다른 Survivor 영역으로 이동시킨다. (1개의 Survivor 영역은 반드시 빈 상태가 된다.)

4. 이러한 과정을 반복하여 계속해서 살아남은 객체는 Old 영역으로 이동된다.

 

객체의 생존 횟수를 카운트하기 위해 Minor GC에서 객체가 살아남은 횟수를 의미하는 age를 Object Header에 기록합니다. 그리고 Minor GC 때 Object Header에 기록된 age를 보고 이동 여부를 결정합니다.

또한 Survivor 영역 중 1개는 반드시 사용이 되어야 합니다. 만약 두 Survivor 영역에 모두 데이터가 존재하거나, 모두 사용량이 0이라면 현재 시스템이 정상적인 상황이 아님을 파악할 수 있습니다.

 

Major GC의 동작 방식

Young 영역에서 오래 살아남은 객체는 Old 영역으로 이동됨을 확인할 수 있었습니다. 그리고 Major GC는 객체들이 계속 이동되어 Old 영역의 메모리가 부족해지면 발생하게 됩니다. Young 영역은 일반적으로 Old 영역보다 크기가 작기 때문에 보통 GC가 0.5초에서 1초 사이에 끝납니다. 그렇기 때문에 Minor GC는 애플리케이션에 크게 영향을 주지 않습니다. 하지만 Old 영역은 Young 영역보다 크며 Young 영역을 참조할 수 있습니다. 그렇기 때문에 Major GC는 일반적으로 Minor GC보다 시간이 오래 걸리며, 10배 이상의 시간을 사용합니다.

 

 

  Minor GC Major GC
대상 Young Generation Old Generation
실행 시점 Eden 영역이 꽉 찬 경우 Old 영역이 꽉 찬 경우
실행 속도 빠름 느림

 

반응형

댓글