https://www.youtube.com/watch?v=FMUpVA0Vvjw
Why Review ?
Java의 Garbage Collector 메모리 관리 소프트웨어에 대해 알아봄으로써, JVM 기반의 소프트웨어에 대해 더 자세히 알고싶다.
목차
1. GC가 왜 필요한가?
1-1. GC의 장점
1-2. GC의 단점
2. GC 알고리즘
3. JVM의 GC
4. JVM GC 튜닝 맛보기
1. GC가 왜 필요하지?
- 프로그램이 동적으로 할당햇던 메모리 영역(Heap 영역) 중 필요없게 된 영역(어떤 변수도 가리키지 않음)을 알아서 해제한다.
- 자바와 자바스크립트에서는 GC가 동작한다.
- 제대로 해제하지 않으면 Memory Leak 이 발생하기도 함.
1-1. GC의 장점
- 메모리 누수가 사라진다.
- 해제된 메모리에 접근을 막고, 해제한 메모리를 또 해제하는것을 막는다.
1-2. GC의 단점
- GC 작업은 순수 오버헤드
- 개발자는 언제 GC가 메모리를 해제하는 모른다.
- 실시간성이 매우 중요할경우 GC에게 맡기는것은 불안정할 수 있다.
2. GC 알고리즘 - Reference Counting 과 Mark And Sweep
GC는 어떻게 해제할 동적 메모리 영역들을 알아서 판단하는 2가지 알고리즘에 대해 알아보자
먼저 Root Space : 스택변수, 전역변수 등 Heap 영역 참조를 담은 변수를 의미한다. 를 알아둔다.
Refrence Counting은 Heap 영역에 선언된 객체들이 각각 reference Count라는 숫자들이 담겨있는것을 의미한다.
- 해당 객체에 접근할 수 있는 방법이 하나도 없다면, 즉, refererence Count가 0 이라면 가비지 컬렉션의 대상이 된다.
- 하지만, Reference Couting 알고리즘의 문제점은 순환참조 문제가 있습니다. Root Space 속에서의 HeapSpace의 연결을 모두 끊는다면, 서로가 서로를 참조하는 경우에는 reference Count가 계속 1로 유지되기에 memory leak 이 발생됩니다.
Mark And Sweep 알고리즘은 순환 문제를 해결합니다.
- 이 알고리즘은 Root 에서부터 해당 객체에 접근가능한지를 해체의 기준으로 삼는다.
- 연결된 객체들은 마킹한다. Mark, Reachable
- 연결이 끊겨진 객체들은 지운다. Sweep., UnReachable
- Sweep 이후에 분산된 메모리가 정리되며, Memory Compaction이 일어나며 Memory 파편화가 사라진다. 하지만 필수는아님.
- Mark And Sweep을 통해 순환참조되는 객체도 모두 지울 수 있다.
- 자바와 자바스크립트가 Mark And Sweep 방식을 사용한다.
- 단점은 객체의 reference Count가 0 이되면 지워지는것과 달리 Mark and Sweep은 의도적으로 GC를 실행시켜야한다.
즉, 애플리케이션 실행과 GC 실행이 병행된다
- Application의 사용성을 유지하면서 효율적으로 GC를 실행시키는것이 어려운 방식.
JVM의 GC - JVM 구조, 크게 3가지 영역으로 구성된다.
1. Class Loader
- JVM은 byte code를 읽고 class 정보를 Heap/Method 영역에 저장한다.
2. JVM Memory
- 실행중인 프로그램을 Memory에 올려둔다.
3. Executuon Engine
-바이트 코드를 네이티브 코드로 변환시켜주고 GC를 실행시켜주는 실행엔진.
- JVM은 OS로부터 Memory를 할당받은 후 해당 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.
- 모든 쓰레드가 공유하는 영역으로 Method Area와 Heap 영역이 있고,
- 각 쓰레드마다 모두 각각 생성하는 JVM Language Stack, PC register, Native Method Stacks가 있다. 이것들은 각각의 스레드가 사라지면 사라진다.
Method Area
- 프로그램의 클래스 구조를 메타데이터처럼 가지며, 메서드의 코드들을 저장해둔다.
- Heap은 어플리케이션 실행중에 생성되는 객체 인스턴스를 저장하는 영역이다.
- 이 Heap 영역이 Garbage Collector에 의해 정리되는 부분.
- Stack은 메서드 호출을 스택프레임이라는 블록으로 쌓아, 로컬변수, 중간연산 결과들이 저장되는 영역이다.
- pc register은 스택 프레임의 저장 주소를 가지고 있다.
- Native Method Stack는 c/c++의 Low level의 코드를 실행한다.
JVM의 Root Space가 어디있는지 아는것이 중요하다.
아래 3가지에 존재한다.
- Stack의 로컬변수
- Method Area의 Static 변수
- Native Method Stack의 JNI 참조
이제 JVM GC의 RootSpace가 어디있는지 알고있으니, Mark And Sweep 방식에 대해 알 수 잇다.
1. 의도적으로 GC를 실행시켜야한다.
- JVM GC에게는 실행시켜야하는 타이밍은 'JVM의 Heap'영역'에 의해 결정된다.
- Heap영역에는 Young Generation과 Old Generation 이 나누어져잇다.
- Young Generation은 또 다시 세 영역 으로나뉜다. Eden, Survivial0, Survival1 로 나뉜다.
- Eden : 새롭게 생성된 객체 들이 할당된다.
- survival 0, 1 : Minor GC로부터 살아남은 객체들이 존재하는것. 특별규칙은 survival 0, survival1 둘중하나는 반드시 비어있어야한다.
- 계속해서 새로운 객체가 추가되면 Eden이 꽉차는 시점이 발생되는데 이때, Minor GC가 발생.
- 이때 Root Space에서 Reachable이라 판단된 객체는 필요한 객체이므로 Survival 0 으로 옮겨져서 살아남는다. Survival 뜻이 살아남은 객체라는 뜻이다. 그리고 객체의 bit가 1로 증가한다. Age-bit라고도 한다. 그리고 이동할때마다 Age-bit는 1 씩 증가한다.
- 또다시 Eden 공간이 꽉차서 Reachable한 것들과 Survival0 에 있는 것들은 모두 Survival 1 로 이동한다.
- 이때 한번 더 이동되면서 Age-bit가 3이 되면,(혹은 일정수준)으로 이동되면 오래 살아남을 객체구나 라고 판단하여 Old generation으로 이동된다. Java 8 기준으로는 age-bit가 15가 되면 Promotion이 진행되고 Old generation으로 이 동한다.
- 언젠가 Old Generation이 꽉 차면, Major GC가 작동하며 Mark and sweep이 발생하게 된다.
- 왜 굳이 Old, Young으로 나누었을까? 분석해보니 대부분의 객체 수명이 매우 짧다. GC도 결국비용이기에 메모리의 특정부분만 탐색하여 해제하는것이다.
- Old Generation은 1가지 영역이다.
2. 어플리케이션 실행과 GC 실행이 병행된다.
- "Stop the World"라는 개념란? "GC를 실행하기 위해 JVM이 어플리케이션 실행을 멈추는것이다."
- 이 Stop the World를 최소화시키는것이 어려운 최적화 작업이다.
- Serial GC
- 하나의 쓰레드로 GC를 실행한다.
- Stop the World 시간이 길다.
- 싱글 쓰레드 환경 및 Heap이 매우 작을떄 사용한다.
- Parallel GC는 여러개의 쓰레드로 가비지 컬렉션을 실행한다.
- 그에 따라 Serial GC보다 빠르다.
- JAVA 8 의 default 실행방식
- Stop the World 시간을 최소화하기 위해 고안되다.
- GC 작업을 어플리케이션과 동시에 실행, 하지만 메모리와 CPU를 많이 사용하고, 무엇보다 Mark And Sweep 과정 후 메모리 파편화를 해결하는 Compaction 이 기본적으로 제공되지 않는다.
- G1 GC가 등장.
- Garbage First (G1)
- Heap을 일정 크기의 Region으로 나누어 사용
- 어떤 영역은 Young, 어떤 영역은 Old generation으로 나눈다.
- 런타임에 G1 GC가 필요함에 따라 영역별 Region 개수를 튜닝한다.
- Java 9 부터 default GC 방식
JVM GC 튜닝 맛보기
- GC 튜닝은 성능의 마지막 단계이다.
- 객체 생성을 줄이려는 코드레벨의 노력이 먼저 필요 ( 예시로 String 대신 StringBuilder를 사용 )
- Old Generation으로 넘어가는 개체 최소화하기
- Major GC 시간을 짧게 유지하기
- 한정된 Heap 영역에 Young, Old Generation을 얼마만큼 할당해야하는지.
- 메모리가 너무 크다면 GC는 적게 일어나겠지만, 메모리가 작다면 GC는 자주 일어나지만 금방 끝난다.
- 어플리케이션의 특성 고려필요.
JVM 튜닝
- initalHeapSize = 65G, MAxHeapSize=10기가
- GC 방식은 Paraell GC.
- JVM 메모리 모니터링. JDK 설치 시 기본제공되는 jstat이라는 툴을 활용
- $ jstat -gcutil -t 8844 (port) 1000 10 (1초에 한번씩 총 10번 모니터링)
- S0(survival 0), S1 (suvirival 1), E (Eden 영역) O (Old Generation) YGC(Young Generation GC 이벤트 수 ), YGCT(Young Generation 총 GC 시간), FGC ( 전체 이벤트 GC 수) FGCT ( 전체이벤트 GC 시간), GCT ( 전체 GC 시간)
1. 정보를 출력하는 10초 사이에 애플리케이션 글 작성요청을 보낸다.
2. 새로운 객체 할당되며 Eden 영역의 사용률이 늘어난다.
3. Young GC 영역에서 총 19번의 GC가 0.314 초 동안 실행되다. => 0.314 / 19 = 0.016 -> Minor GC가 0.016 초
Major GC는 총 3번 실행되고, 0.291 초 동안 실행되었다. Major GC가 0.291 /3 = 0.097초
4. DB Connection이 1초이상 타임아웃 상황이라면 GC가 장애원인이 될 수 있음.
5. jstat gccapacity 명령을 통해 프로세스가 heap 영역을 얼마나 사용중인지 정확한 수치를 알 수 있다.
$ jstat -gccapacity -t 8844 1000 10
-. NG로 시작되는 지표들은 new, Young Generation. OG로 시작하는 지표들은 Old generation 영역을 의미
- CMN은 영역의 최소 할당크기. CMX 는 영역의 최대 허용크기를, C는 영역의 현재크기를 각각 KB로 나타낸다.
- 현재 NGC는 약 323 MB,. OGC는 686 MB 사용중.
- 이와 같이 분석 후 JVM Option을 다르게 설정한다. Heap 영역 크기 조절, Young Generation 영역의 크기, 앞서 살펴본 GC 실행방식들을 변경할 수 있다.
'무언가에 대한 리뷰 > 테크영상리뷰' 카테고리의 다른 글
[무언가에 대한 리뷰][테크영상리뷰][쉬운코드]DB 인덱스(DB index) !! (0) | 2023.10.09 |
---|---|
[무언가에 대한 리뷰][테크영상리뷰][10분 테코톡] 라라, 제로의 데이터베이스 인덱스 (0) | 2023.10.07 |
[무언가에 대한 리뷰][우아콘2020] 수십억건에서 QUERYDSL 사용하기 (0) | 2023.10.04 |
[무언가에 대한 리뷰][10분 테코톡] 조조그린의 Thread Pool (0) | 2023.10.04 |
[무언가에 대한 리뷰][영상리뷰][10분 테코톡] 알렉스, 열음의 멀티스레드와 동기화 In Java (0) | 2023.10.04 |