- C와 같은 프로그래밍 언어에서 메모리 할당 및 할당 해제는 수동 프로세스입니다.
- 반면에, Java에서 메모리 할당 해제 프로세스는 Garbage Collection에 의해 자동으로 처리가 됩니다.
[기본 프로세스]
Step1 : Marking
- Garbage Collector가 사용중인 메모리 조각과 사용하지 않는 메모리 조각을 식별합니다.
- 마킹 단계에서 모든 개체들을 스캔하다보니 많은 시간이 소요될 수 있습니다.
Step2 : Normal Deletion
- 참조되지 않은 개체를 제거하여 참조된 개체와 포인터를 여유 공간으로 남깁니다.
- 메모리 할당자는 새 개체를 할당할 수 있는 여유 공간 블록에 대한 참조를 보유합니다.
Step2a : Deletion with Compacting
- 성능 향상을 위해 참조된 나머지 개체들을 압축할 수도 있다.
- 참조된 개체를 함께 이동하면 새 메모리 할당이 훨씬 쉽고 빨라진다.
Generational Garbage Colection
- JVM의 모든 개체들은 확인하고 마킹한 뒤 삭제하는 작업은 계속 할당되는 개체들을 관리하기엔 매우 비효율 적이다. 그러나, 응용프로그램에 대한 경험적 분석에 따르면 대부분의 개체는 수명이 짧다.
JVM Generations
- 힙 영역은 더 작은 세대로 나뉘게 된다.
[stop-the-world]
- GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것을 말한다.
- stop-the-world가 발생하면 GC를 실행하는 쓰레드를 제외한 나머지 쓰레드는 모두 작업을 멈춘다.
- 어떤 GC 알고리즘을 사용하더라도 발생하며 보통의 GC 튜닝이란 stop-the-world의 시간을 줄이는 것을 말한다.
[GC가 만들어진 전제조건]
- 대부분의 객체는 금방 접근 불가능 상태(unreachable)가 된다.
- 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
- 이 가설을 'weak generational hypothesis'라 한다.
이 가설의 장점을 최대한 살리기 위해서 HotSpot VM에서는 크게 2개로 Young영역과 Old영역으로 물리적 공간을 나누었다.
Young 영역
- 새롭게 생성된 객체의 대부분이 여기에 위치한다.
- 이 영역에서 가득차서 객체가 사라질때 Minor GC가 발생한다고 한다.
Old 영역
- 접근 불가 상태가 되지 않고 Young영역에서 살아남은 객체가 여기로 복사된다.
- 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다.
- 이 영역에서 객체가 사라질 때 Major GC(or Full GC)가 발생한다.
- Permanaent Generation 영역은 Method Area라고도 한다.
객체나 intern된 문자열 정보를 저장하는 곳이다.
Old 영역에서 살아남은 객체가 영원히 남아 있는 곳은 절대 아니며 이 영역에서 GC가 발생할 수 있는데, 여기서 GC가 발생해도 Major GC이 횟수에 포함된다.
- Card Table : Old 영역에 있는 객체가 Young 영역의 객체를 참조하는 경우를 처리하기 위해 512chunk 할당된 공간이다.
- Young 영역의 GC를 실행할 때, Old 영역에 있는 모든 객체의 참조를 확인하지 않고, 이 카드 테이블만 뒤져서 GC 대상인지 식별한다.
- Card Table은 write barrier를 사용하여 관리함다.
write barrier는 Minor GC를 빠르게 할 수 있도록 하는 장치로 약간의 오버헤드는 발생하지만 전반적인 GC 시간은 줄어들게 된다.- write barrier : computer storage system에 쓰기 시퀀스의 특정 순서를 적용하기 위한 매커니즘이다.
Garbage Collector에서는 세대 불변성을 유지하기 위해 모든 저장 작업 직전에 컴파일러에서 내보내는 코드 조각이다.
- write barrier : computer storage system에 쓰기 시퀀스의 특정 순서를 적용하기 위한 매커니즘이다.
[Young 영역의 구성]
영역
- Eden 영역
- Survivor 영역(2개)
처리 절차
- 새로 생성한 대부분의 객체는 Eden 영역에 위치한다.
- Eden 영역에서 GC가 한번 발생한 후 살아남은 객체는 Survivor 영역 중 하나로 이동된다.
- Eden 영역에서 GC가 발생하면 이미 살아남은 객체가 존재하는 Survivor 영역으로 객체가 계속 쌓인다.
- 하나의 Survivor 영역이 가득 차게 되면 그 중에서 살아남은 객체들을 다른 Survivor 영역으로 이동한다.
- 이 과정을 반복하다 계속 살아남은 객체가 Old 영역으로 이동하게 된다.
- Survivor 영역이 가득 차면 비어있는 다른 Survivor로 살아남은 객체들을 옮기는 작업이 발생한다
👉 두 개중 하나의 영역은 반드시 비어있는 상태이어야 한다.
- Survivor 영역이 가득 차면 비어있는 다른 Survivor로 살아남은 객체들을 옮기는 작업이 발생한다
[Old영역에 대한 GC]
- Old 영역은 기본적으로 데이터가 가득 차면 GC를 실행한다.
- JDK 7을 기준으로 5가지 방식이 있다
- Serial GC
- 운영 서버에서 절대로 사용하면 안되는 방식이다.
데스크톱의 CPU 코어가 하나만 있을 때 사용하기 위해서 만든 방식이기 때문이다.
- 운영 서버에서 절대로 사용하면 안되는 방식이다.
- Parallel GC
- Parallel Old GC(Parallel Compacting GC)
- Concurrent Mark & Sweep GC
- G1 GC
- Serial GC
[Serial GC]
- Young 영역은 Eden영역에서 살아남은 객체들이 Survivor에 쌓이고 가득 찬 경우, 비어있는 다른 Survivor로 옮겨지게 된다.
이 과정을 반복하다 살아남은 객체들이 Old 영역으로 넘어간다. - Old 영역은 mark-sweep-compact라는 알고리즘을 사용한다.
Old 영역에 살아 있는 객체들을 식별(Mark)하고 heap의 앞 부분부터 확인하며 살아 있는 것만 남긴다.(Sweep) - 마지막으로 각 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눈다.(Compaction)
[Parallel GC]
- Serial GC와 기본 알고리즘은 갖지만 GC를 처리하는 쓰레드가 여러개라는 차이가 있다.
[Parallel Old GC]
- JDK 5 update 6부터 제공한 GC 방식이다.
- Parallel GC와 차이점은 Old 영역의 GC 알고리즘이 Mark-Summary-Compaction이라는 것이다.
[Concurrent Mark & Sweep GC]
- Initial Mark 단계에서는 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는 것으로 끝낸다.
- Concurrent Mark 단계에서는 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인한다.
- Remark 단계에서는 Concurrent Mark단계에서 새로 추가되거나 참조가 끊긴 객체를 확인한다.
- Concurrent Sweep 단계에서는 쓰레기 정리를 실행한다.
- 장점 : stop-the-word 시간이 짧다.
- 단점: Compaction 단계가 기본적으로 제공되지 않으며 다른 방식보다 메모리와 CPU를 더 많이 사용한다.
[G1GC(Garbage-First Garbage Collector)]
- CMS GC를 대체하기 위해서 만들어 졌다.
- JDK 7에서 정식으로 G1 GC를 포함하여 제공되었고 JDK 9부터 G1GC가 default로 제공됩니다.
- 많은 양의 메모리가 있는 다중 프로세서 시스템을 대상으로 합니다.
장점
- stop-the-world 없이도 여유 메모리 공간을 압축하는 기능을 제공한다.
- 해당 세대의 일부분에 대해서만 압축하면 된다.
- Heap 크기가 클수록 잘 동작한다.
- CMS에 비해 개선된 알고리즘을 사용하고, 처리 속도가 더 빠르다.
- Garbage로 가득찬 역역을 빠르게 회수하여 빈 공간을 확보하므로 GC 빈도가 줄어든다.
단점
- 공간 부족 상태를 조심해야 한다.
(Minor GC, Major GC를 수행하고 나서도 여유 공간이 부족한 경우)- Full GC가 발생하는데 Single Thread로 동작하며 이것은 heap 영역 전반적으로 GC가 발생한다는 것이다.
- 작은 Heap 공간을 가지는 애플리케이션에서는 제 성능을 발휘하지 못하고 Full GC가 발생한다.
- 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간인 Humonogous 영역은 제대로 최적화되지 않으므로 해당 영역이 많으면 성능이 떨어진다.
G1GC의 Heap 구조
- Humonogous : Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간
- Available/Unused : 아직 사용되지 않은 Region
G1GC의 Cycle
G1GC는 위 그림처럼 Young-only Phase와 Space Reclamation Phase를 반복한다.
사이클중 모든 원은 stop-the-world가 발생한 것을 나타낸 것이고, 원의 크기에 따라 소요 시간이 달라진다고 보면 된다.
파란 원은 Minor GC(=Young GC, Evacuation Phuse)가 진행함에따라 stop-the-world가 발생한 것이다.
주황 원은 Major GC(=Old GC, ConcurrentCycle)이 진행하면서 객체를 마킹 및 기타 과정을 하기 위해 stop-the-world가 발생한 것이다.
빨간 원은 Mixed GC를 진행함에 따라 stop-the-world가 발생한 것이다.
Minor GC
연속되지 않은 메모리 공간에 Young Generation이 Region 단위로 메모리에 할당되어 있다.
Young Generation에 있는 유효 객체를 Survivor Region이나 Old Generation으로 copy 또는 move를 한다.
이 단계에서 stop-the-world가 발생하고 Eden Region과 Survivor Region의 크기는 다음 Minor GC를 위해 다시 계산된다.
Minor GC를 마치고 나면 Eden영역에서 Survivor영역으로 이동하거나 Survivor영역에서 Suvrivor영역으로 이동했다는 청록색 영역이 생긴다.
Major GC(Old GC)
G1의 Old GC는 5개의 과정이 존재한다.
- Initial Mark
- Root Area Scan
- Concurren Mark
- Remark
- Copy / CleanUP
Initial Mark
Initial Mark 단계는 Survivor 영역에서 Old 영역을 참조하고 있을 수 있는 영역들을 마킹한다.
Survivor 영역을 마킹하는 단계이기 때문에 Minor GC에 의존적이며, stop-the-world가 발생한다.
Root Area Scan
Initial Mark 단계에서 찾은 Survivor Region에 대한 GC 대상을 식별한다.
멀티 쓰레드로 동작하여 Minor GC가 발생하기 전에 동작을 완료한다.
Concurrent Mark
힙 내의 모든 Old 영역에 대해 GC 대상을 식별한다.
또한 Region에 모든 객체가 Garbage라 판단되면 Remark 단계에서 즉시 제거된다.
stop-the-world를 발생시키지 않지만 Young GC에 의해 중단될 수 있다.
Remark
Remark 단계는 Concurrent Mark에서X표시한 영역을 바로 회수하여, stop-the-world가 발생한다.
또한, Concurrent Mark 단계에서 작업하던 Mark를 이어서 작업하여 완전히 끝내버린다.
이때 Snapshot-At-The-Beginnig (stop-the-world 이후 살아있는 객체에만 마킹하는 알고리즘) 기법을 사용하기 때문에 CMS GC보다 더 빠르다.
Copy / CleanUp
stop-the-world가 발생하는 단계로 Remark 단계에서 제거한 Region 이외에 livenless가 가장 낮은 Region을 선택하고 새로운 Region으로 이동 또는 복사한다.
이 과정은 Old 영역 뿐만 아니라 Young 영역도 포함될 수 있다.
After Copy / CleanUp
Copy / Clean Up 단계 이후 새로운 Region으로 이동하고 Compact 되었다.
Reference.
https://d2.naver.com/helloworld/1329
https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
https://steady-coding.tistory.com/590
https://gona.tistory.com/61
'Java' 카테고리의 다른 글
Iterable와 Iterator에 대해 (0) | 2022.07.03 |
---|---|
자바에서의 제네릭과 와일드카드 (0) | 2022.05.12 |
JVM 구조와 Java의 실행방식 (0) | 2022.05.07 |
멀티 스레드의 스레드들이 객체를 공유하는데 임계영역의 문제를 해결하지 않은 경우 (0) | 2022.04.09 |
Java char타입의 초기화를 할 때 '' 불가한 이유 (0) | 2022.04.02 |