-
Java Garbage Collectorjava 2021. 5. 10. 12:21
Overview
www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
위 글 번역하며 공부
Purpose
Hotspot JVM과 GC가 상호작용하는 기본 원리를 공부한다. 일단 이 기본을 공부하고 GC를 모니터링하며 종류별 GC에 대해 공부하자.
Java Technology and the JVM
Java Overview
Java Runtime Edition
java를 다운로드 받으면 Java Runtime Envirnment(JRE)를 얻는다. JRE는 Java Vitual Machine(JVM), 코어클래스, 플렛폼 라이브러리로 구성되어 있다. 3가지 구성요소가 Java를 돌리는 데 반드시 필요하다.
Java Programming Language
자바는 아래와 같은 특징을 가진 프로그래밍 언어다.
- OS독립 : 자바애플리케이션은 바이트코드로 컴파일 되어 있다. 이는 class파일로 저장되고 JVM에 로드된다. 이 덕분에 애플리케이션은 OS나 디바이스에 종속되지 않고 실행된다.(단 JVM자체는 OS종속 소프트웨어)
- 객체지향 : 자바는 객체지향언어이면서 C나 C++의 특징을 가져오고 향상시켰다.
- 자동화 GC : 자바는 메모리 할당과 해제를 자동으로 해줘 프로그래머가 애플리케이션 로직에 집중할 수 있도록 한다.
- 다양한 내장 라이브러리 : 입출력, 네트워크 등 프로그램에 필수적인 다양한 기능을 내장 라이브러리에 포함한다.
Java Development Kit
JDK는 자바애플리케이션을 개발하기 위한 통합 도구다.
Java Vitual Machine
JVM은 어떤 프로그램을 실행하기 위한 가상머신 프로그램이다. 동일한 자바 애플리케이션을 작성해도 JVM이 현재 OS에 맞는 low level명령어를 만들기 때문에 자바는 OS독립적인 언어라고 말할 수 있다.(JVM만 OS 종속적)
Hotspot JVM Architectrue
자바애플리케이션 성능튜닝에 중요한 구성은 3가지가 있다.
- Heap : 객체가 저장되는 장소다. GC의 관리 대상이다. 대부분의 성능튜닝이 Heap의 크기 설정과 GC선택에 달려있다.
- JIT compiler : 최적화가 잘 되어 있어 실제로 건드릴 일은 거의 없다.
Performance Basics
- Responsiveness(응답성) : 애플리케이션이 요청을 받았을 때 얼마나 빠르게 응답하는가?
- Throughput(처리량) : 단위 기간 동안 시스템이 얼마나 많은 일을 처리하는가?
Garbage Collection
GC는 heap영역을 관찰하면서 참조자가 없는 객체의 메모리를 해제한다. 이때 참조가 없다는 뜻은 더이상 해당 객체의 레퍼런스를 갖는 변수가 없다는 의미다. C언어와 달리 java GC는 JVM이 자동으로 수행한다.(프로그래머가 하지 않는다)
사전 정보
GC의 동작을 이해하기 전에 객체의 특징을 확인해보자. 그래프를 통해 확인해보면 시간이 지남에 따라 살아 남은 객체의 수는 현저히 떨어지게 된다. Minor collections에서 대부분의 객체가 가비지가 됨을 알 수 있다.
즉, GC가 메모리 해제를 위해 heap영역을 스캔할 때 모든 객체를 전부 스캔하는 건 비효율적이라는 거다. 객체의 생성시점에 맞춰 메모리를 논리적으로 분리하고 최근에 생성된 객체는 자주 GC대상인지를 스캔하고 상대적으로 오래 살아남은 객체는 가끔 GC대상인지 체크하는 것이 훨씬 효율적일 것이다.
Mark and Sweep
GC가 메모리를 해제하기 전에 GC는 Heap에 있는 객체를 reachable과 unreachable로 구분해야 한다. 이때 unreachable을 가비지로 판단해 메모리를 해제하는 것이다. 이때 객체를 구분하는 과정을 Mark라 하고 메모리를 해제하는 작업을 Sweep라고 한다.
Mark
모든 객체에는 1bit의 mark bit가 있다. 객체가 최초로 생성될 때 이 값은 0인 false로 세팅된다. 이제 GC가 Root Set에서 DFS를 수행하고 방문하는 모든 객체의 mark bit의 값을 true로 만든다. 여기서 Root Set이란 각 스레드 stack의 지역변수 중 참조형 또는 클래스의 static 변수 등등이 있다. (GC가 Root Set을 관리하는 방법은 굉장히 딥하므로 나중에 조사하자)
Sweep
이제 Heap영역을 스캔하며 객체의 mark bit를 확인한다. 여기서 mark bit가 false인 객체를 unreachable로 판단해 메모리를 해제하는 것이다.
JVM Generations
위 사전정보를 바탕으로 JVM의 Heap영역은 Young, Old, Permanent 이렇게 3가지로 나뉜다.
Stop the World Event
GC가 일어날 때를 Stop the World라 하고 이때 모든 자바애플리케이션의 수행이 멈춘다. 오직 GC스레드만 활동한다.
Young Generation
YG에는 모든 새로 생성된 객체가 최초로 저장된다. YG가 꽉 찼을 때 minor GC가 발생하게 된다.
YG는 다시 Eden, survivor1, survivor2로 나뉜다. minor GC의 과정과 객체 aging을 자세하게 살펴보자
1) 새로 생성된 객체는 일단 Eden영역에 저장된다
2) Eden이 꽉 차게 된다면 minor GC가 Eden에서 발생하고 참조가 남은 객체는 survivor1영역으로 이동한다
3) Eden이 다시 꽉차게 된다면 이번엔 Eden과 survivor1영역에 대해 mionr GC를 수행한다. 이때 살아남은 객체는 survivor2영역으로 옮긴다.(minor GC 발생 이후 Eden과 하나의 survivor는 clear한 상태가 된다)
4) 이렇게 survivor를 이동한 횟수를 기억하고 특정 횟수를 넘는 객체는 OG로 영역을 옮긴다(프로모션)
Old Generation
OG에는 오래 살아남은 객체가 저장된다. OG에서 일어나는 GC를 major GC라 한다.
Java Garbage Collectors
GC의 기본 컨셉은 전부 위에서 설명한 것과 동일하다. 이제 애플리케이션의 특성을 고려해 세부사항을 설정해야 한다.
Heap과 관련된 설정
- Xms : 초기 Heap사이즈를 설정할 수 있다.
- Xmx : 최대 Heap사이즈를 설정할 수 있다.
- Xmn : YG의 사이즈를 설정할 수 있다.
- XX:PermSize : PG의 사이즈를 설정할 수 있다
- XX:MaxPermSize : PG의 최대 사이즈를 설정할 수 있다.
Serial GC(-XX:+UseSerialGC)
OG에서 GC가 발생할 때 mark, sweep, compact작업을 수행한다. serial GC는 싱글코어 환경을 전제로 동작한다. compact작업을 한다는 의미는 메모리 효율을 최대화하기 위함이다. 즉, 단일코어에 제한된 메모리 영역에서 사용하는 GC이며 성능에선 불리하다.
Parallel GC
YG에서 GC를 수행할 때 멀티스레드를 이용해 병렬처리한다.(-XX:ParallelGCThreads=<desired number>)
추측이지만 mark단계와 sweep단계는 동시성을 고려하지 않고 멀티스레드로 처리하면 훨씬 높은 효율을 낼 수 있을 것 같다. 다만, compaction작업은 아무래도 병렬처리는 힘들지 않을까싶다.
G1 GC
기본적인 YG, OG 및 mark and sweep는 비슷하다고 생각하면 될 것 같다. 특이한 점은 G1 GC인 경우 마치 운영체제가 메인메모리를 페이지 단위로 나눠 메모리 관리를 효율적으로 하듯이 G1 GC 또한 동일한 메모리 크기로 Heap영역을 나눴다는 점이다. unreachable 객체에 대해 메모리를 반납한 후 reachable을 compaction하는 과정이 보다 효율적이고 관리하기 싶다는 특징이 있다. 마찬가지고 멀티스레드로 작업할 수 있다.
그래서 어떻게 수행된다고?
모든 객체는 Heap의 Eden영역에 저장된다.
Eden영역이 꽉차면 GC가 수행되고 시스템의 Root Set부터 reachable을 마킹한다.
Eden영역에서 unreachable을 suvivor로 옮긴다. 이 과정을 반복한다.
suvivor영역을 특정횟수 이상 옮겨간 객체는 OG영역으로 옮긴다.
OG영역을 GC할 때는 mark-sweep-compaction작업을 수행한다.
OG영역은 sweep할 메모리 영역이 크므로 오버헤드가 크다.
'java' 카테고리의 다른 글
애너테이션 정리 (0) 2021.05.11 컬렉션 정리 (0) 2021.05.10 BeanUtils copyProperties 사용법 및 주의사항 (0) 2021.05.04 java 실행과정 (0) 2021.05.01 JWT를 이용한 인증 (0) 2021.04.16