I need to know a little Garbage Collection

개요

 프로그래밍 언어에는 2가지 언어가 있다. Managed 언어와 Unmanaged 언어로 구분된다. 이 둘의 가장 큰 차이점은 개발자가 직접 메모리를 할당하고 해제하는 관리 여부로 결정된다. 

대표적인 Unmanaged 언어로 C, C++ 이 있고, Managed 언어로는 Java, Python, Go, C# 등이 있다.

이번글에서는 알아서 memory 를 관리해주는 Garbage Collection 에 대해서 정리해보려고한다. 언어마다 Garbage Collection 알고리즘이나 기법이 다르다. 이 글에서 다루고자 하는 언어는 Java 이고 JVM 이다.

What?

Heap 영역에서 어떤 object 가 사용중인지 아닌지 확인하고 사용하지 않는 object 는 삭제한다. 사용중인 object 는 referenced object 로 program 에서 해당 object 에 대한 pointer 를 유지하고 있는 것을 의미한다. 사용하지 않는 object 는 unreferenced object 로 더 이상 program 에서 pointer 를 유지하고 있지 않는 것을 의미한다. 따라서 unreferenced object 의 memory 를 회수할 수 있다.

C 나 C++ 은 수동으로 memory 를 allocate 하고 deallocate 한다. Java 에서는 Garbage Collector 에 의해서 자동으로 관리된다. 

첫 번째 : Marking

Garbage Collector 가 memory 의 사용여부를 확인하는 작업이다.

referenced objects 들은 파란색이다. Unreferenced objects 는 주황색이다. 모든 objects 는 Marking 단계에서 scan 작업을 거친다. 만약 전체 시스템의 모든 objects 를 scan 해야 한다면, 이 작업은 시간이 많이 드는 작업이다.

두 번째 - 1 : Normal Deletion

Normal deletion 은 unreferenced objects 지우지만 free space 로 남겨두고 새로운 object 가 할당될 수 있또록 한다.

두 번째 - 2 : Deletion with Compacting

성능향상을 위해서, unreferenced objects 를 삭제하는 것에 이어서 남겨진 referenced objects 들을 compact 작업을 할 수 있다. referenced object 들을 인접한 영역으로 옮기는 작업을 통해서 새롭게 할당 되는 object 들이 쉽고 빠르게 할당할 수 있도록 한다.

Why?

JVM 에서 모든 objects 들을 Marking 하고 Compact 하는 작업은 비효율적이다. 점점더 objects 들이 할당되고, objects 들이 계속 증가할 수록 Garbage Collection 시간은 길어질 것이다. 하지만 application 경험적인 분석에 따르면 대부분의 objects 들은 생명이 굉장히 짧다.


시간이 지남에 따라서 할당된 objects 는 계속해서 줄어든다. 대부분의 objects 의 생명주기는 굉장히 짧다.

Generations

Heap 영역은 세대에 따라서 작은 부분으로 나누어져 있다. Young, Old or Tenured, Permanent 로 구성되어 있다.
Young Generation 모든 새로운 object 들이 할당된다. Young Generation 이 가득차게 되면 minor garbage collection 이 발생한다. Young generation 의 garbage collection 은 매우 빠르게 이루어진다. minor garbage collection 에서 살아남은 object 들은 Old Generation 으로 이동한다.

Old Generation 은 오래 살아 남은 objects 들을 저장하는 곳이다. 일반적으로, Young Generation 에서 정해진 일정기간이 지나면 Old Generation 으로 이동된다. 최종적으로는 Old Generation 도 Garbage Collection 의 대상이 된다. Old Generation 이 가득차게 되면 또한 Garbage Collection 이 발생하게 되는데 이를 major garbage collection 이라고 한다.

Permanent Generation 은 application 에서 사용되는 class 와 method 들의 metadata 가 저장되는 곳이다.  또한 Java SE library class 와 method 가 저장되는 곳이다. main memory heap 과 분리된 특수영역이다. Method Area 가 Permanent Generation 에 속한다. JVM 이 다른 class 가 필요하고 현재 사용하지 않는 class 를 발견할 경우 해당 class 는 garbage collections 이 된다. 이를 full garbage collection 이라고 한다.

Stop the World

모든 minor garbage collections, major garbage collections, full garbage collections 은 "Stop the World" event 이다. 모든 application thread 는 garbage collections 이 끝날 때 까지 중지된다. 일반적으로 major garbage collections 은 minor garbage collections 보다 느리다. 따라서 major garbage collections 은 최소화 되어야 한다. 

Process

그렇다면 heap 영역은 왜 Generations 으로 분리 되었을까? 다음을 통해서 확인해보자.
새로운 objects 는 eden 영역에 할당된다. 처음에 survivor 영역은 빈 영역으로 시작한다.

eden 영역이 가득차면, minor garbage collection 이 일어난다.
referenced objects 들은 첫 번째 survivor 영역으로 이동된다. eden 영역의 unreferenced objects 들은 삭제된다. 
다음 minor garbage collection 에서 eden 영역에 같은 일이 일어난다. unreferenced objects 들은 삭제되고 referenced objects survivor 영역으로 이동된다. 그러나, 이번에는 두 번째 survivor 영역으로 이동된다. 또한, survivor 영역으로 이동될 때 object 들의 age 가 증가된다. 각각 age 가 다른 object 들이 survivor 영역에 공존할 수 있다.

다음 minor garbage collection 에서 같은 작업이 반복된다. 하지만, survivor 영역간의 이동이 일어난다.

지속적인 minor garbage collection 후, aged object 가 임계 age 값에 도달했을 때(그림에서는 8),  8에 도달한 object 는 Young Generation 에서 Old Generation 으로 Promotion 된다.
minor garbage collection 은 지속적으로 일어나고 promotion 도 계속해서 지속된다.
major garbage collection 은 Old Generation 에 수행 된다.

Available Collectors

Serial Collector

모든 garbage collection 작업 시 single thread 를 사용한다. thread 간의 communication overhead 를 줄일 수 있다는 점에서 효율적이다.

multiprocessor hardware 의 이점을 누릴 수 없기 때문에 single processor machine 에서 사용하기 최적화 되어있다. 적은 데이터(대략 100MB) 를 다루는 application 을 multiprocessor 환경에서 유용할 수 있긴하다. 특정 hardware 나 OS 설정에서 default 로 선택할 수 있다. 혹은 -XX:+UseSerialGC 설정을 통해서 명시적으로 설정할 수 있다.

Parallel Collector

throughput collector 라고도 알려져있다. multiple threads 를 사용하여 garbage collection 의 속도를 향상시킨다. multiprocessor 또는 multithreaded hardware 를 사용하는 medium-sized 에서 large-sized 데이터를 다루는 application 에 적합하다. -XX:+UseParallelGC option 을 통해서 설정할 수 있다.

major garbage collection 을 parallel 로 작업할 수 있도록 하는 Parallel compaction 기능이 있다. major collection 은 single thread 로 수행된다. -XX:+UseParallelGC, -XX:-UseParallelOldGC 로 enable 혹은 disable 할 수 있다.

Garbage-First (G1) Garbage Collector

concurrent collector 이다. 대부분 concurrent collectors 들은 비싼 작업을 수행한다. small machines 에서 대용량 large multiprocessor machines 아울르게 설계되었다. 높은 처리량과 높은 확률로 stop the world 시간을 줄일 수 있는 기능을 제공한다.

G1 은 기본적으로 대부분의 hardware 와 OS 설정으로 채택되어 있다. 또는 명시적으로 -XX:+UseG1GC 를 통해서 설정할 수 있다.

The Z Garbage Collector

줄여서 ZGC 라고 불리며, 확장가능한 low latency garbage collector 이다. 비싼작업을 application thread 의 중지 없이 concurrently 작업한다. 

최대 중지 시간은 몇 milliseconds 이지만 처리량(throughput) 을 어느정도 희생해야 한다. low latency 를 요구하는 application 을 위해서 설계 되었다. 중지 시간은 heap size 와 무관하다. heap size 는 8MB 에서 16TB 까지 지원한다. -XX:+UseZGC option 을 통해서 활성화 할 수 있다.

Selecting Collector

엄격한 stop the world 시간의 단축이 필요하지 않다면, 우선 VM 이 collector 를 선택하도록 해도 괜찮다.

필요하다면, Heap sizie 를 조정해야 한다. Heap size 를 조정했음에도 성능 향상 목표를 달성하지 못한다면, Collector 를 아래 가이드라인에 따라 선택해야 된다.

- 적은 데이터 (대략 100MB) 를 가진 application 이라면, Serial Collector 를 선택하라. -XX:+UseSerialGC
- Single processor 환경에서 application 이 동작하고 stop the world 시간의 요구사항이 없다면 Serial Collector 를 선택하라. -XX:+UseSerialGC
- application 의 성능이 최우선이고 stop the world 시간이 1 ~ 2 혹은 길어도 괜찮다면, VM 이 collector 를 선택하도록 하거나 Parallel Collector 를 선택하라. -XX:+UseParallelGC
- 전반적인 처리량 (throughput) 보다 응답 시간이 가장 중요한 요소이고 stop the world 시간이 계속해서 짧은 시간을 유지해야 된다면 G1 GC 를 선택해라. -XX:+UseG1GC
- 응답시간이 중요한 순위이라면 ZGC 를 선택해라. -XX:UseZGC

해당 가이드라인은 collector 를 선택하는 시작점을 제공하는 것 뿐이다. 실질적 성능 향상은 application 에 의해서 유지되는 live data 의 양 즉, Heap size 설정에 의존하고 있다. 또한 사용가능한 processors 의 속도와 수의 영향을 많이 받는다.

collector 가 기대하는 성능을 달성하지 못한다면, 첫 번째로는 Heap 과 generation size 를 조정해야 한다. 여전히 성능이 충족하지 않는다면, 다른 collector 를 시도해야 한다.
concurrent collector 는 pause-time (stop the world) 시간을 줄이는데 좋다.
parallel collector 는 multiprocessor hardware 에서 전반적인 처리량 (throughput) 늘리기 좋다.


참조


댓글

이 블로그의 인기 게시물

About JVM Warm up

About idempotent

About Kafka Basic

About ZGC

sneak peek jitpack

Spring Boot Actuator readiness, liveness probes on k8s

About Websocket minimize data size and data transfer cost on cloud

About G1 GC

대학생 코딩 과제 대행 java, python, oracle 네 번째