JVM Structure
자바 개발자라면 한정된 메모리를 효율적으로 사용하고 최적의 퍼포먼스를 내기 위해 JVM(Java Virtual Machine)을 잘 알아둬야 한다. 프로그램의 성능은 알고리즘 외에도 메모리 관리와 매우 밀접하게 연결되어 있다.
JVM은 자바 프로그램의 생명 주기 전체를 관장하며 리소스를 할당하고 메모리를 관리한다.
Class Loader System
Class Loader는 자바 바이트 코드를 읽어서 JVM의 Execution Engine이 사용할 수 있도록 Runtime Data Area에 적재하는 역할을 한다.
즉, 자바 클래스들은 한 번에 메모리에 올라오는 것이 아니라, 런타임 시 필요할 때 동적으로 클래스를 로딩하게 된다.
로딩
-
Application Class Loader
- Application Classpath에서 클래스를 읽고 로드
링크
-
Preparation
- 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드, 메서드, 인터페이스들을 나타내는 데이터 구조 준비
-
Resolve
- 심볼릭 메모리 레퍼런스를 메서드 영역에 있는 실제 레퍼런스로 교체
초기화
- 클래스 변수들(static)을 적절한 값으로 초기화
메모리(Runtime Data Area)
런타임 데이터 영역은 JVM이 OS 위에서 구동되면서 할당받는 메모리 영역이다.
-
쓰레드마다 개별적 할당
- PC register
- Stack
- Native Method Stack
-
모든 쓰레드 공유
- Heap
- Runtime Constant Pool
PC register
- PC(Program Counter) 레지스터는 각 스레드마다 하나씩 존재하며 스레드가 시작될 때 생성
- 현재 스레드가 수행 중인 코드의 주소를 갖는다.
Stack
- 각 스레드마다 하나씩 존재하며 스레드가 시작될 때 생성
- 스택 프레임(Stack Frame)이라는 구조체만 저장 가능하며, push, pop으로 동작
Native Method Stack
- 자바 외에 언어로 작성된 네이티브 코드를 위한 스택
Method Area
- class 영역이라고도 불리며, 모든 스레드가 공유하는 영역
- JVM이 시작될 때 생성되고, JVM이 읽어들인 정적으로 판단할 수 있는 데이터의 대부분을 보관
Runtime Constant Pool
- 각 클래스와 인터페이스의 상수뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블
- JVM은 Runtime Constant Pool을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조
Heap
- 동적으로 인스턴스가 저장되는 공간으로 가비지 컬렉션의 대상
- 모든 쓰레드가 접근 가능하며, 성능 이슈에도 많은 지분 차지
Execution Engine
Interpreter
- Class Loader를 통해 JVM 내 Runtime Data Area에 배치된 바이트 코드를 인터프리터가 명령어 단위로 한 줄씩 읽어나가며 실행
JIT 컴파일러
-
코드를 처음 읽는 시점(동적 시점)에 중복 코드를 찾아 Native Code로 번역
-
이후 인터프리터가 중복 코드를 만날 때 인터프리팅을 건너뛰고, 캐시에 저장된 Native Code를 사용
GC(Garbage Collection)
- 더 이상 참조되지 않는 Heap 영역의 인스턴스들을 자동으로 삭제해 주는 역할
JNI(Java Native Interface)
- 자바 애플리케이션에서 C, C++, 어셈블리로 작성된 함수를 사용할 수 있는 방법 제공
- Native 키워드를 사용한 메서드 호출
네이티브 메서드 라이브러리
-
C, C++로 작성된 라이브러리
- ex) Thread.currentThread();