Programming-[Backend]/Java

[자바 기초] 3. 자바의 동작원리 : Garbage Collection

컴퓨터 탐험가 찰리 2021. 12. 15. 22:07
728x90
반응형

 

1. Garbage Collection(GC) 개념

 

기본 개념은 쉽다. 어떤 인스턴스가 생성되어 메모리 공간을 차지한 상태에서 해당 인스턴스가 프로그램에서 사용되지 않게 되었다면(null 처리 되었거나 해당 인스턴스를 참조하는 부분이 없다면) 해당 인스턴스가 Garbage가 된다. 이런 불필요한 메모리 낭비를 방지하기 위해서 개발자가 직접 인스턴스를 찾아서 제거하는 것이 아니라, JVM이 불필요한 메모리를 정리해주는 기능이 Garbage Collection 이다.

 

이전 글에서 자바는 Heap memory 영역에 인스턴스를 저장하고 Young Generation, Old Generation 부분이 나뉘어서 관리된다고 배웠다. 그리고 Young Generation에서 Minor GC, Old Generation에서 Major GC가 일어난다고 했었다. 이것은 다음 JVM의 2가지 대전제를 기본으로 한다.

 

  1. 대부분의 객체는 쉽게 Garbage가 된다.
  2. Old generation 영역의 객체가 Young Generation의 객체를 참조하는 일은 드물다.

 

드문 경우라고 전제하였으나 Old Generation 영역에 있는 객체가 Young Generation 영역의 객체를 참조하는 경우를 대비해서 Old Generation 영역에는 512 bytes의 기본 단위(Chunk)로 구성된 카드 테이블이 존재한다. 이는 Young Generation 영역에서 Minor GC 실행 시, Old Generation 영역에서 참조되는 Young Generation 영역의 인스턴스를 모든 Old Generation 영역을 검사하는 것이 비효율적이기 때문이다. 그래서 Minor GC 시에 카드 테이블만 조회하여 GC 대상을 식별한다.

Ref1-그림1. Old Generation 영역의 Card Table. 

추측하기로 각 사각형이 Chunk 영역이고 화살표 2개에 해당하는 참조 정보가 있는 chunk가 초록색 사각형으로 표현된 것 같다.


 

2. GC의 동작방식

 

공통 동작 방식

세부동작 방식은 영역별, 적용 알고리즘별로 다르지만 공통적으로 따르는 2단계는 다음과 같다.

 

  1. Stop the world
  2. Mark and Sweep

 

1. Stop the world

JVM이 GC를 실행하기 위해서 애플리케이션의 실행을 멈추는 작업이다. 이때는 GC를 실행하는 쓰레드 외 다른 모든 쓰레드는 작업이 중단된다. 애플리케이션 중단 시간 최소화를 위해서 이 stop the world 작업의 소요시간을 줄이기 위해 다양한 알고리즘을 적용한다.

 

2. Mark and Sweep

Stop the world 이후, GC가 스택의 모든 변수 또는 접근 가능한 Reachable 객체를 스캔한다. 사용되지 않는 메모리를 식별하는 과정이 Mark, 이 메모리들을 제거하는 과정을 Sweep 이라고 한다.

 

 

 

 

Minor GC 동작방식

 

 

Ref1 - 그림2. Minor GC 동작방식

 

 

앞선 글에서 Young Generation 영역은 Eden과 Survivor 영역으로 나뉜다고 했었다.

  • Eden 영역 : 새로 생성된 객체가 할당되는 영역
  • Survivor 영역 : 최소 1번의 이상의 GC 이후 남은 객체가 존재하는 영역

이 영역이 Minor GC의 구성요소가 되며, 동작원리는 다음과 같다.

 

  1. 인스턴스가 계속 생성되어 Eden 영역이 포화된다.
  2. Stop the world -> Mark and Sweep 실행
  3. 2.에서 살아남은 객체가 첫 Survivor 영역으로 이동
  4. 첫 Survivor 영역 포화 -> Mark and Sweep으로 살아남은 객체가 두번째 Survivor 영역으로 이동
  5. 일정 횟수(age) 이상 살아남은 객체를 Old Generation 영역으로 이동(이것을 Promotion 이라고 한다.)

 

Survivor 영역에서 객체가 살아남은 횟수를 age 라고 하며, 이 age를 Object Header에 기록한다. Survivor 영역의 제한 조건으로 Survivor 영역 중 반드시 1개는 사용되어야 하고, 나머지는 비어 있어야 한다. 만약 두 Survivor 영역에 모두 데이터가 존재하거나 모두 사용량이 없다면 비정상으로 간주된다.

 

-참고 : 객체 할당 기술, bump the pointer, TLABs(Buffer)

HotSpot JVM(JVM의 여러 구현체 중 오래 살아남은 JVM 구현체 이름, CPU 코어가 한 개뿐인 사용자를 위해 만들어졌다고 한다)에서는 Eden 영역에 객체를 빠르게 할당하기 위해서 bump the pointer, TLABs(Thread-Local Allocation Buffers)라는 기술을 사용한다.

  • bump the pointer : Eden 영역에 마지막으로 할당된 객체의 주소를 캐싱하여 새로운 객체 할당 시 마지막 주소 바로 다음 주소를 사용하는 기술
  • TLABs : 멀티 쓰레드에 인스턴스를 할당해야하는 경우 동기화 문제를 해결하기 위해 적용되는 기술이다. 각 쓰레드의 Eden 영역에 TLAB이라는 영역을 미리 할당해놓고 객체가 생성되면 TLAB 영역에 인스턴스를 할당해서 여러 쓰레드에 빠르게 인스턴스들을 할당할 수 있게 된다.

 

 

 

 

Major GC(FUll GC) 동작방식

 

Young Generation 영역에서 Promotion으로 넘어온 인스턴스들에 의해서 Old Generation 영역의 메모리가 부족해지면 Major GC가 발생한다. 그런데 크기가 작은 Young Generation에서의 Minor GC에 비해 Major GC는 10배 이상의 시간이 소모될 수 있다.

 

 


 

3. GC의 소요시간과 알아두면 좋은 점들

 

 

참조 2 : YABOOG 블로그 - 자바 메모리 관리 - 가비지 컬렉션

참조 2에서는 GC의 설정을 하고, GC가 동작할 때/동작을 막았을 때 일어나는 현상에 대해 프로그램 동작 및 성능 측정을 했다. 주요 내용이나 배울점은 아래와 같이 요약된다.

 

-System.gc()를 호출하면 가비지 컬렉션이 일어나게 할 수 있다. 그러나 직접적인 호출을 하여 모든 스레드가 중단되도록하면 안된다. 절대 하지말자.

-System.gc() 메서드는 C언어로 만들어져서 다음과 같이 표기된다.

public native void gc();

 

-GC가 동작하지 못하게 참조가 살아있는 인스턴스를 계속 만들면 Out of memory 에러가 발생한다.

- -Xmx16m -verbose:gc 라는 설정으로 힙 영역의 최대사이즈를 16MB로 설정할 수 있다고 한다.

 

 

 

 


 

참조

 

1. 망나니 개발자님 블로그 - [Java] Garbage Collection(가비지 컬렉션)의 개념 및 동작 원리 (1/2)

https://mangkyu.tistory.com/118

 

2. YABOOG 블로그 - 자바 메모리 관리 - 가비지 컬렉션

https://yaboong.github.io/java/2018/06/09/java-garbage-collection/

728x90
반응형