1) 가비지 컬렉션이란?
- 자바의 가장 큰 특징 중 하나가 개발자가 직접 메모리 관리를 할 필요가 없다는 점입니다.
자바에서는 JVM의 가비지 컬렉터가 더 이상 사용하지 않는 메모리를 해제해주는데,
이것을 '가비지 컬렉션(Garbage Collection)(이하 GC)'이라고 합니다.
2) 가비지 컬렉션 과정
- GC 과정에 대해 알아보기 전에 알아야 할 용어가 있습니다. 바로 'stop-the-world'입니다. 'stop-the-world'는 GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것을 의미합니다. stop-the-world가 발생하면 GC를 실행하는 스레드를 제외한 나머지 스레드들은 동작을 멈춥니다. GC 작업을 완료한 이후에 다시 작업을 시작합니다. 일반적으로 GC 튜닝이란 이 stop-the-world 시간을 줄이는 것입니다.
- 자바는 일반적으로 프로그램 코드에서 메모리를 명시적으로 해제하지 않습니다. 하지만 명시적으로 해제하기 위해 객체를 null로 처리하거나 System.gc()를 사용할 수 있습니다. 이 때, null로 지정하는 것은 괜찮지만, System.gc()를 사용하는 것은 시스템의 성능에 큰 영향을 미칠 수 있으므로, System.gc() 메서드는 절대로 사용하면 안됩니다.
- 가비지 컬렉터는 2가지 전제하에 만들어졌습니다. 이를 Weak generational hypothesis라고 합니다.
그것은 다음과 같습니다.
(1) 대부분의 객체는 금방 접근 불능(unreachable) 상태가 된다.
(2) 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
이 가설의 장점을 살리기 위해 HotSpot JVM에서는 물리적 공간을 크게 2가지 영역으로 나눴습니다.
그것은 Young영역과 Old영역입니다.
- Young 영역: 새롭게 생긴 객체는 Young 영역에 위치합니다. Young 영역에 위치한 객체는 대부분 금방 사라집니다.
이를 Minor GC라고 합니다. Young 영역은 크게 3가지 영역으로 구성되는데, 한 개의 Eden 영역과
두 개의 Survivor 영역입니다.
- Old 영역: 접근 불가능 상태가 되지 않은 객체는 Old 영역으로 이동합니다.
Old영역의 크기는 Young영역에 비해 크고, 크기가 큰만큼 GC는 적게 발생합니다.
Old영역에서 일어나는 GC를 Major GC라고 합니다.
- 이를 그림으로 나타내면 다음과 같습니다.
- 다음은 각 영역의 처리절차에 대해서 좀 더 자세히 알아보겠습니다.
2-1) Young 영역
- 위에서 Young 영역은, 하나의 Eden영역과 두 개의 Survivor 영역, 즉 3개의 영역으로 구성된다고 말씀드렸습니다.
각 영역의 처리 절차를 기술하면 다음과 같습니다.
(1) 새로 생성한 대부분의 객체는 Eden영역에 위치합니다.
(2) Eden 영역에서 GC가 한 번 발생한 후, 남은 객체는 Survivor 영역으로 이동합니다.
(3) 하나의 Survivor 영역이 꽉 차게 되면, 살아남은 객체가 다른 Survivor 영역으로 이동합니다.
이 때, 두 개의 Survivor 영역 중 한 영역은 반드시 비어 있어야 합니다.
(4) 이 과정을 계속 반복하다가, 살아 남은 객체는 Old 영역으로 이동합니다.
2-2) Old 영역
- Old 영역은 기본적으로 데이터가 가득차면 GC를 실행합니다. GC 방식에 따라 처리 절차가 달라지는데,
Java 7을 기준으로 총 5개의 GC 방식이 존재합니다.
- Serial GC
- Parallel GC
- Parallel Old GC
- Concurrent Mark & Sweep(CMS) GC
- G1 GC
- 이 중에서 Serial GC는 운영 서버에서 절대로 사용하면 안됩니다.
그 이유는 Serial GC는 데스크톱 CPU의 코어가 한 개 있을 때 만들어진 것이므로,
Serial GC를 사용하면, 어플리케이션의 성능이 많이 떨어지기 때문입니다.
(1) Serial GC
- Serial GC는 Young 영역은 Serial 방식, Old 영역은 Serial Mark-Sweep-Compact 방식을 사용합니다.
Mark-Sweep-Compact 방식이란, 우선 Old 영역에 살아 있는 객체를 식별(Mark)하고, 살아 있는 것만 남긴 후(Sweep),
살아 있는 객체들이 힙의 앞 부분부터 쌓이도록(Compact) 하는 방식입니다.
Serial GC는 한 개의 스레드만 사용하므로, 메모리가 작고, CPU 코어가 적을 때 적합합니다.
(2) Parallel GC
- Parallel GC는 기본적으로 Serial GC와 알고리즘은 같습니다.
단, Parallel GC는 Young 영역에서 여러 개의 쓰레드를 사용하므로, Serial GC보다 빠르게 동작합니다.
Old 영역에서는 싱글 스레드로 동작합니다.
Parallel GC는 다른 말로 Throughput GC라고도 합니다.
Parallel GC는 메모리가 충분하고, CPU 코어가 많을 때 적합합니다.
(3) Parallet Old GC
- Parallel Old GC는 Parallel GC와 비교해서 Old 영역의 알고리즘이 다릅니다.
Parallel Old GC는 Old 영역에서 Mark-Summary-Compact 알고리즘을 사용합니다.
(4) Concurrent Mark&Sweep(CMS) GC
- Concurrent Mark & Sweep GC는 Inital Mark - Concurrent Mark - Remark - Concurrent Sweep 단계를 거쳐서
동작합니다. Initial Mark 단계에서는 싱글 스레드로 Live Object를 구별하며,
Concurrent Mark 단계에서는 싱글 스레드로 Intial Mark에서 구별된 Live Object들을 참조하고 있는 Object들을
추적해서 Live 여부를 판별합니다. 이 때, 애플리케이션은 수행되고, GC 스레드 외에 Working 스레드는
애플리케이션 수행이 가능합니다.
그 다음 Remark 단계에서는 멀티 스레드를 사용하며, 애플리케이션이 중지된 상태에서 이미 Marking 된 Object
들을 다시 추적해서 Live 여부를 확정합니다.
마지막으로 Concurrent Sweep 단계에서는 싱글 스레드를 사용하여, 애플리케이션은 수행되고, 최종 Live로
판명된 Object들을 제외한 Dead Object들을 지웁니다.
- CMS GC의 장점은 Stop-the-world 시간이 짧다는 점입니다. CMS GC를 다른 말로 Low-latency GC라고도 합니다.
반면 단점은
(1) 다른 GC 방식보다 메모리와 CPU를 많이 사용하며,
(2) Compaction 단계가 제공되지 않는다
는 점입니다.
(5) G1 GC
- 마지막으로 G1 GC에 대해 알아보겠습니다. G1 GC는 Young, Old 영역의 구분이 없어진 GC 방식입니다.
G1 GC는 바둑판과 같은 영역에 객체를 할당하고, GC를 실행합니다.
그러다가 해당 영역이 꽉 차면 다른 영역에 객체를 할당하고, GC를 실행합니다.
G1 GC는 CMS GC를 대체하기 위해 만들어졌습니다.
G1 GC의 가장 큰 장점은 성능이라고 할 수 있습니다.
참고
자바의 신 2권
JVM Performance Optimizing 및 성능 분석 사례
'Java' 카테고리의 다른 글
String, StringBuffer, StringBuilder 클래스 (0) | 2022.08.17 |
---|---|
리플렉션 (0) | 2022.08.17 |
자바에서 소스 코드가 실행되는 과정 (0) | 2022.08.10 |
HashMap vs HashTable vs ConcurrentHashMap (0) | 2022.08.06 |
자바 스레드(1) (0) | 2022.08.06 |