JVM 관련
JVM 전체 구조
- Class Loader
- 런타임(최초 호출 시점)에 .class(바이트코드) 읽어 class load
- Execution Engine
- Interpreter, JIT 둘 다 사용
- Memory Layout
- PC register
- JVM level에서 현재 실행하고 있는 instruction의 주소 보관.
- CPU level의 PC와 기능은 같지만 추상화 수준이 다름.
- thread 별로 각각 가지고 있음. (CPU register가 thread local 한 것 처럼 당연히)
- Method Area
- Memory layout 상 PermGen, MetaSpace 안에 속하는 일부 영역.
- OS의 text segment와 유사함. (결국 실행 대상 code, instructions를 보관하는 영역이라는 뜻)
- run-time constant pool
- field and method data
- the code for methods and constructors
- PC register
**참고) static 변수는 Method Area 안에 들어가는게 아니라(JVM spec에 이런 내용은 없다),
Method Area를 감싸고 있는 PermGen 영역에 들어간다. (이 것도 java 8 부터는 PermGen이 사라지면서 달라졌다.)
Method Area 등은 JVM spec에서 사용하는 용어이고,
실제 구현체인 Hotspot JVM에서는 이와 1:1 대응되지 않는 다른 용어(PermGen, Metaspace)를 사용 할 수 있다.
스펙과 실제 구현이 다를 수 있기 때문에 스펙 자체에 뭐가 들어가고 뭐가 안들어가고를 너무 타이트하게 볼 필요는 없다.
JVM Heap 구조와 GC
- Eden -> [S0 <> S1] -> Old 순으로 이동한다. (Old == Tenured)
- Survivor 영역을 S0, S1 2개로 구성하고 GC 마다 살아남은 객체가 S0 <> S1을 왔다 갔다 하도록 만드는 이유는, 메모리 스페이스를 재정렬해서 연속된 메모리 영역을 확보하기 위함.
- https://dzone.com/articles/understanding-the-java-memory-model-and-the-garbag
- GC 동작 방식에 대해서는 d2 참조 https://d2.naver.com/helloworld/1329
JVM PermGen과 MetaSpace
- Java 7까지는 PermGen(Permanent Generation) 영역이고, Java 8 부터는 해당 영역이 MetaSpace 영역으로 대체되었다. (https://openjdk.org/jeps/122)
- 둘 다 목적은 로드된 class metadata 보관. (class metadata에 대해서는 아래 별도 항목 참조)
- PermGen 영역의 문제점은,
- 1. memory leak : GC가 제대로 되지 않아, MaxPermSize가 다 차면서 OOM 발생 할 수 있다는 점.
- 2. MaxPermSize가 항상 고정이라는 점. (물론 아예 크게 잡으면 되긴 하지만...)
- https://www.baeldung.com/java-permgen-space-error
- PermGen과 MetaSpace의 메모리 고갈 비교
- PermGen이라고 GC가 안되는 것은 아니고, leak 원인은 다양하지만 주로 classloader 관련해서 문제가 있는 듯
- PermGen 영역과 MetaSpace 영역의 차이는,
- PermGen 영역은 Heap의 일부이나, MetaSpace는 Native Memory 영역으로, OS가 관리.
- Java 7에서는 PermGen 영역에 일부 Java Object (static 변수, interned string)가 들어갔지만, Java 8 부터는 기존 PermGen에 들어가던 Java Object는 모두 Old 영역에 들어가고, MetaSpace에는 Meta 정보만 들어감.
- MetaSpace는 기본이 unbounded 크기라 해당 머신의 메모리를 다 쓰지 않는 한 OOM 발생 가능성 낮음.
- MetaspaceSize(임계치)에 도달하면 자동으로 GC 트리거하여 dead classloaders and classes 제거.
- https://www.baeldung.com/java-permgen-metaspace
** 참고) PermGen에서 저장하고 있는 static 변수는 reference 만이다. Object itself가 아니다. (sof) 동적배열 static 변수가 PermGen 고갈을 유발한다고 볼 수는 없다.
변수, 객체의 resolve 시점?
- compile time에 resolve 되는 것
- 상수, Constant Expression 뿐이다. (조건을 만족하는 String, primitive type들만 해당)
- Compile-time constant expressions of type String are always "interned" so as to share unique instances, using the method `` String.intern()``
- static 변수는? Runtime : 최초 호출 시점에 resolve
- 예상과 달리 Application 실행 시점에 자동으로 resolve 되는 것이 아니다.
- 해당 클래스 최초 접근 시점에 class loader가 .class 파일 읽어 load 하면서 metadata도 메모리에 올라가기 때문.
- Class 로딩과 초기화는 다르다.
- 상기 JVM 구조에서, Class Loader가 클래스를 로드하는 과정은 3가지 sub-step으로 구성된다.
- Loading - `` Cls.class`` 접근하는 순간 발생하며 .class 파일 읽어와 Method Area에 올린다.
- Linking - 검증 작업
- Initialization - Cls 생성자나 상수가 아닌 static 멤버에 접근하는 순간(== resolve가 필요한 순간) 발생하며 static 필드 초기화, static 블럭 수행
- 이 과정은 thread-safe 하다. (동시에 여러 thread에서 여러번 초기화 되지 않는다)
- 어떻게 접근하느냐에 따라 Loading까지만 일어나고 static 초기화는 아직 일 수도 있지만, 보통은 `` Cls.class``만 하는 경우가 잘 없으므로 3가지 과정이 연속해서 일어난다고 보면 된다.
- 상기 JVM 구조에서, Class Loader가 클래스를 로드하는 과정은 3가지 sub-step으로 구성된다.
'Java Stack > Java' 카테고리의 다른 글
[Java] LocalDateTime : 날짜 시간 처리 관련 (0) | 2019.07.10 |
---|---|
[Java] Enum (0) | 2019.06.06 |
[Java] Jackson ObjectMapper Serialization (0) | 2019.05.15 |
[Java] Stream API 노트 (0) | 2017.03.09 |
[Java] lambda 기본 개념 (0) | 2017.02.07 |