새로운 내용을 공부할 때
새로운 내용의 공부를 시작할 때 용어의 정의를 이해하지 못하거나 정확하게 알지 못한다면 그 용어가 포함된 문장을 이해하지 못합니다.
작은 단어 하나가 내용을 이해하지 못하게 하기 때문에 용어를 정확하게 이해하는 것이 중요합니다.
GC가 동작하면 STW가 발생하는 이유
오늘 학습해볼 내용은 GC와 Stop the world입니다.
Stop The World
Stop the world는 JVM에서 가비지 컬렉션을 수행할 때 모든 애플리케이션 쓰레드의 실행이 중단되는 현상을 합니다.
GC는 메모리 관리를 위해 주기적으로 불필요한 객체를 정리해야하며, 이 과정에 메모리 상태를 일관성 있게 유지하기 위해 모든 애플리케이션 쓰레드를 일시적으로 정지시켜야합니다.
GC를 실행하는 쓰레드를 제외한 나머지 모든 쓰레드는 작업이 멈추는데 왜 일시정지가 필요할까?
왜 일시정지를 하는 이유
- 메모리 상태의 일관성 유지
-
GC는 힙(Heap) 메모리에서 사용되지 않은 객체를 식별하고 제거해야합니다. 이 과정에서 메모리의 상태를 정확하게 파악해야 하기 때문에, 동시에 실행 중인 애플리케이션 쓰레드가 메모리에 접근하여 변경하는 것을 방지합니다. 이를 위해 GC는 모든 쓰레드를 정지시킵니다.
- 루트 집합 탐색
-
GC는 먼저 루트 집합(root set)에서부터 시작하여 도달 가능한 객체를 탐색합니다. 루트 집합에는 JVM 스택, 전역 변수, 레지스터 등이 포함됩니다. 이 탐색 과정에서 메모리가 변경되면 일관성 문제가 초래할 수 있습니다. 안전하게 루트 탐색을 하기 위해 일시 정지가 필요합니다.
그러면 루트 탐색은 어떠한 방식으로 이루어지는가
런타임 데이터 영역에 쓰레드가 차지하는 영역과, 객체를 생성 및 보관하는 하나의 큰 힙, 그리고 메소드 영역(현 metaspace)가 있습니다.
힙에 있는 객체들에 대한 참조는 4가지 종류 중 하나입니다.
- 힙 내의 다른 객체에 의한 참조 (D)
- Java 스택 즉 Java 메서드 실행시 사용하는 지역변수와 파라미터에 의한 참조 (A)
- 네이티브 스택, 즉 JNI에 의해서 생성된 객체에 대한 참조(C)
- 메서드 영역의 정적 변수에 의한 참조(B)
D를 제외하고 2,3,4번이 root set
으로 GC가 탐색을 시작하는 부분입니다.
**Reachability 와 unreachable **
-
파란색으로
root set
에서 참조를 시작하여 참조 사슬에 속한 객체는 GC 대상이 아닌 (reachable
)이라고 합니다. -
초록색으로 칠한 대상이며
reachable
이 직접 참조하지 않고 참조 당하는 객체도 GC 대상인(unreachable
)이라고 합니다.
Compaction 과정
GC가 동작하여 사용하지 않은 객체 메모리를 해제한 이후에는 메모리 공간이 비효율적으로 남게 됩니다.
힙 영역 내의 빈 공간들을 조각나있기 때문에 다시 큰 블럭으로 만들어 사용할 수 있도록 디스크 조각모음
하는 과정이 발생합니다.
이때 살아있는 reachable
객체의 메모리 위치가 변경되며 다시 참조할 수 있도록 연결하는 과정이 필요합니다.
즉 안전한 메모리 조각 모음을 하기 위해 stop the world가 필요합니다..
Metaspace와 PermGen에 대해
사실 Metaspace와 PermGen에 대해서 학습을 해도 크게 이해가 되지 않았습니다.
이번에 GC가 동작하면서 왜 STW가 발생하게 되는지 공부를 하다보니 덩달아 이해가 될때 정리해봅니다.
GC가 동작하기 위한 트리거는 다음과 같습니다.
- 명시적인 GC 요청
- 힙 영역의 사용 상태
- YoungGeneration에 가득찼을때
- OldGeneration에 가득찼을때
- Metaspace 영역에 클래스로더가 언로드 될때
자바 7은 PermGen 은 고정 크기로 JVM이 시작할 때 설정된다. 필요에 따라 동적으로 확장되지 않는다.
스프링과 같이 리플렉션을 사용하여 동적 클래스를 생성하는 경우 PermGen이 가득차서 OOM
이 될 수 있다.
기존에는 PermGen 영역에 있는 메타데이터는 FullGC 시에만 청소되었으므로 가비지 컬렉션 주기와 성능에 영향을 미치게 됩니다.
자바 8이 되면서 Metaspace로 변경되면서 GC의 시점으로 보면 FullGC가 되어야 청소할 수 있던 구역이 클래스로더가 언로드가 되면 청소가 됩니다.
- 클래스로더가 X클래스를 로드후 인스턴스를 2개 만듭니다.
- 힙 메모리에는 id, a, b 에 대한 인스턴스가 저장됩니다.
- 메타스페이스 영역에는 X 클래스에 대한 메타데이터 한개만 저장됩니다.
- 클래스 로더가 Y클래스를 로드하고 인스턴스를 1개 만듭니다.
- 힙 메모리에 c에 대한 인스턴스가 저장됩니다.
- 이제 c를 사용하지 않지만 Metaspace에는 아직 메타 정보가 남아있습니다.
- a,b,c 모두 unreachable 즉 모두 참조되지 않은 객체가 되었지만 메타 스페이스에는 남아있습니다.
- 클래스로더 변수 id가 unreachable이 되고 GC가 동작하면서 인스턴스를 제거하면
- 그때 메타 스페이스에서 X,Y에 대한 정보를 삭제합니다.
기존에는 FullGC로만 제거 할 수 있었던 클래스 메타데이터를 이제는 언로드를 통해 삭제할 수 있다보니 좀더 메모리 관리가 편해집니다.
댓글남기기