이번 포스팅은 우아한Tech의 던의 JVM의 Garbage Collector 영상 정리 및 다른 블로그들의 내용을 합쳐서 정리한 내용입니다.
1. Garbage Collector란?
동적으로 할당한 메모리 영역 중 사용하지 않는 영역을 탐지하여 해제하는 기능
- Stack : 정적으로 할당한 메모리 영역
- 원시 타입의 데이터가 값과 함께 할당. Heap 영역에 생성된 Object 타입의 데이터 참조 값 할당.
- Heap : 동적으로 할당한 메모리 영역
- 모든 Object 타입의 데이터가 할당. Heap 영역의 Object를 가리키는 참조 변수가 Stack에 할당
Stack에 있던 데이터가 pop 되면서 날라가고, heap 영역에 객체 타입의 데이터만 남는데 이런 객체를 Unreachable Object라고 한다.
2. Garbage Collector 과정
1. Garbage Collector가 Stack의 모든 변수를 스캔하면서 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다. (Mark)
2. Reachable Object가 참조하고 있는 객체도 찾아서 마킹한다. (Mark)
3. 마킹되지 않은 객체를 Heap에서 제거한다. (Sweep)
가비지 컬렉터의 과정을 Mark and Sweep라고 한다.
heap의 구조를 먼저 살펴보면 다음과 같다.
Heap은 New Genration과 Old Generation 영역으로 나누어져 있음
새로운 객체는 Eden(에덴) 영역에 할당된다.
Eden 메모리가 다 사용되면 GC 발생(Minor).
살아남은 Eden 영역의 Reachable 객체는 Survival 0으로 옮겨진다. Eden 영역의 Unreachable 객체는 메모리에서 해제
이 과정이 계속해서 반복되고 Survival 0 영역이 꽉차면 Survival 0 영역에 대해서 Mark and Sweep 과정이 일어나게 된다. 여기서 살아남은 Reachable 객체는 Survivial 1 메모리로 옮겨진다.
Survival 1 영역으로 이동한 객체는 Age 값이 증가한다.
그리고 Eden에서 다시 GC가 일어나면 살아남은 객체는 Survival 1으로 이동시켜준다. 결국 Survival 0와 Survival 1영역 중 한곳은 비어있게 된다.
이 과정을 계속 반복하다가 Age 값이 특정 값 이상이 되면 Old Generation 영역으로 옮겨지는데 이 과정을 "Promotion"이라고 한다.
이 과정을 반복하면서 Old Generation이 꽉 차게 되면 가비지 컬렉터가 발생하게 된다. 이때 발생하게 되는 Garbage Collector는 Major Garbage Collector 라고 한다.
지금까지의 과정을 반복하면서 Garbage Collector가 메모리를 관리한다.
3. Garbage Collector의 종류 4가지
■ Serial GC
- GC를 처리하는 스레드가 1개이다.
- CPU 코어가 1개만 있을 때 사용하는 방식
- Mark-Compact collection 알고리즘 사용
- 군데 군데 남은 데이터를 한곳에 모아서 메모리 파편화를 방지
■ Parallel GC
- GC를 처리하는 스레드가 여러개이다.
- Serial GC보다 빠르게 객체를 처리할 수 있다.
- Parallel GC 메모리가 충분하고 코어의 개수가 많을 때 사용하면 좋다.
■ Concurrent Mark Sweep GC (CMS GC)
- Stop-The-World : GC을 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것이다.
- stop-the-world가 발생하면 GC를 실행하는 스레드를 제외한 나머지 스레드는 모두 작업을 멈춘다.
- GC 작업을 완료한 이후에 중단한 작업을 다시 시작한다.
- stop-the-world 시간이 짧다.
- 애플리케이션의 응답 시간이 빨라야할 때 CMS GC를 사용한다.
- 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
- Compaction 단계가 제공되지 않는다.
■ G1 GC
- 각 영역을 Region 영역으로 나눈다.
- GC가 일어날 때 전체 영역(Eden, Survival, Old generation)을 탐색하지 않는다.
- G1 GC는 바둑판의 각 영역에 객체를 할당하고 GC를 실행한다.
- 해당 영역이 꽉 차면 다른 빈 영역에 객체를 할당하고 GC를 실행한다.
- G1 GC는 Stop The World 시간이 짧다.
- Compaction을 사용한다.
참고로, Java 7, 8은 기본 GC로 Parallel GC를 사용하고, Java 9, 10 은 G1 GC를 사용한다고 한다. Java 11부터는 실험적 기능인 Z GC를 사용할 수 있다. 15부터는 정식 기능으로 출시할 예정인 듯 하다.
4. JVM 옵션
GC가 실행되기 전 JVM은 Application의 실행을 멈추는데 Stop-the-World라고한다. GC 쓰레드 이외의 쓰레드들의 작업이 멈춘다. Stop the world 를 줄이는 작업을 GC 튜닝이라 한다.
GC 튜닝이 모든 Java Application 에 필수적인 것은 아니지만, 서버나 코어 엔진의 경우 GC 튜닝이 필요할 수 있다. GC 튜닝을 위해 지켜야할 기본적인 원칙은 GC 튜닝이 로직에 영향을 미치지 않도록 가능한 늦게 수행하고, 객체 생성을 최소화하는 것이다.
즉, GC 튜닝은 Java 코드 최적화와 맞물려 있는 영역이라고도 할 수 있다. 가령 String 의 append 시, + 연산으로 2개 이상의 String 을 더하는 대신, StringBuilder 등을 쓰는 것도 일종의 메모리 튜닝이라고 할 수 있다. 그 외에는 설정적인 부분으로 JVM 옵션으로 메모리 크기를 조절하고 GC 방식을 옵션으로 지정해주는 등이 있다.
(1) 메모리 관련 GC 튜닝을 위한 JVM 옵션
-Xms : JVM 시작 시 힙 영역의 크기
-Xmx : 최대 힙 영역 크기
-XX:NewRatio : New 영역과 Old 영역의 비율. (New 영역의 비율을 전달한다.)
-XX:NewSize : New 영역의 크기
-XX:SurvivorRatio : Eden 영역과 Survivor 영역의 비율 (Survivor 영역의 비율을 전달한다.)
(2) GC 방식 관련 GC 튜닝을 위한 JVM 옵션
- Serial GC :
-XX:+UseSerialGC
- Parallel GC :
-XX:+UseParallelGC
-XX:ParallelGCThreads=value
- Parallel Compacting GC :
-XX:+UseParallelOldGC
- CMS GC :
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=value
-XX:+UseCMSInitiatingOccupancyOnly
- G1 :
-XX:+UnlockExperimentalVMOptions
-XX:+UseG1GC
GC 튜닝은 계속적인 모니터링을 통해서 이루어지는 것이며 지속적인 모니터링과 성능 향상을 위한 노력이 필요하다.
5. PERMANENT 영역
Permanent 영역은 Java8부터 Metaspace 영역으로 변경되었다. 자바8 이전의 Permanet 영역에는 다음과 같은 정보들이 저장되었다.
- Class의 Meta Data
- Method의 Meta Data
- Static Object 변수, 상수
- JVM, JIT 관련 데이터 등
Perm은 JVM에 의해 크기가 강제되던 영역이고, Metaspace영역은 Native메모리 영역으로 JVM이 아닌 OS에 의해 관리되도록 변경되었다.
이 중에서 Static Object는 Metaspace영역이 아닌 Heap영역으로 옮겨져 최대한 GC의 대상이 되도록 변경한 것이다.
거의 그럴일은 없지만 가끔씩 Collection 객체를 static하게 구현하여 값을 계속해서 추가하다가 Perm영역이 가득차서 OutOfmemoryerror permgen space 라는 Error를 경험한적이 있을 수 있다.
static List<Object> list = new ArrayList<>();
즉, 이러한 OOM 에러가 발생하는 현상을 개선하기 위해 기존에 Perm 영역에 저장되던 static Obejct의 변수와, 상수화된 static Object를 Heap영역으로 이동시켜 GC의 대상이 되도록 변경하고, 메타데이터 정보들을 OS가 관리하는 영역으로 옮겨 Perm 영역의 사이즈 제한을 없앤 것이라고 할 수 있다.
자바7 이전
자바8
6. PermGen과 Method Area 차이점
위의 설명에 따르면 두 영역 다 Java Application에 구동되는 클래스와 메소드들이 올라간다. 무슨 차이인가?
Method Area 는 JVM 제품(벤더)에 따라 구현이 다르다.
Hotspot JVM(Oracle)의 Method Area 는 Permanent Area(PermGen)이라고 한다.
REFERENCE
https://1-7171771.tistory.com/140
https://johngrib.github.io/wiki/java8-why-permgen-removed/
'Java' 카테고리의 다른 글
[Java] 코딩 컨밴션 (Code Conventions ) (0) | 2021.04.11 |
---|---|
인텔리제이 javadoc 문서 만들기 (0) | 2021.02.22 |
JVM Stack & Heap (0) | 2021.01.17 |
[Java] Comparable과 Comparator을 이용한 정렬 (0) | 2020.07.19 |
[Java] 자바 Collection (0) | 2020.06.14 |