해당 내용은 Naver D2 Hello에 포스팅 된 JVM Internal과 자바 최적화를 기반으로 내용을 정리하였습니다.
(출처: https://dzone.com/articles/jvm-architecture-explained)
JVM 특징
(출처: https://www.ibm.com/developerworks/java/library/j-dclp1/index.html?S_TACT=105AGX02&S_CMP=EDU)
Class Loader 특징
자바 프로세스가 새로 초기화되면 아래와 같이 Bootstrap
, Extension
, System(Application)
, User-Defined
순으로 차례 차례 클래스를 로드한다.
jre/lib/ext
에 위치한 자바 확장 클래스들이 로딩된다. 다양한 보안 확장 기능등이 여기에서 로드된다.-cp
, -classpath
를 통해 지정이 가능하다.
(출처: https://d2.naver.com/helloworld/1230)
Method Area
모든 클래스 레벨의 데이터(런타임 상수 풀, 필드, 메소드 데이터 등)가 저장되는 공간이며, 모든 JVM 스레드들이 해당 영역을 공유한다.
Heap Area
모든 클래스 인스턴스 및 배열 등이 저장되는 공간이며 JVM 당 하나가 존재한다. Thread Safe하지 않으며 가비지 컬렉션의 대상이다.
Runtime Constant Pool
클래스 파일 포맷에서 constant pool 테이블에 해당하는 영역이며 코드 곳곳에 등장하는 상숫값(클래스명, 인터페이스명, 필드명 등)이 존재한다.
JVM은 코드를 실행할 때 런타임에 배치된 메모리 대신 이 상수 풀 테이블을 찾아보고 필요한 값을 참조한다.
JVM Stack Area
각 스레드별로 분리된 Runtime Stack이 생성된다. 모든 메소드 호출마다 Stack Frame
이라 불리는 엔트리가 생성된다. 각각의 Stack Frame은 아래와 같은
Local Variables
, Operand Stack
을 가지며 현재 실행중인 메소드가 속한 클래스의 런타임 상수 풀에 대한 레퍼런스를 갖는다.
PC Registers
Program Counter라고도 불리는 PC Register는 각 Thread 마다 하나씩 존재한다(스레드 생성될 때 같이 생성). Native Pointer와 Return Address를 가지고 있으며
Method를 수행할 때 현재 수행되고 있는 Instruction 주소(Native Pointer or Method Bytecode의 시작 offset)를 포함하고 있다.
Native Method Stacks
Native method 정보를 유지하며 각 Thread 마다 별도의 Native Method Stack이 생성된다.
Class Loader
를 통해 JVM의 Runtime Data Area
에 배치된 바이트코드는 Execution Engine
에서 실행된다.
자바 프로그램은 바이트코드 인터프리터가 가상화한 스택 머신에서 명령어를 실행하며 시작된다.
Interpreter
바이트코드를 명령어 단위로 읽어서 실행한다. 한줄씩 수행하기 때문에 느리다(하나의 메소드를 수행할 때 마다 매번 Interpretation을 요구한다).
JIT(Just-In-Time)
Interpreter 방식의 단점을 보완하기 위해 도입된 JIT 컴파일러다. 인터프리티드 모드로 실행하는 동안 애플리케이션을 모니터링하면서 가장 자주 실행되는
코드 파트를 발견해 JIT 컴파일을 수행한다. 특정 메소드가 어느 한계치(threshold)를 넘어가면 Profiler가 특정 코드 섹션을 컴파일/최적화한다.
Garbage Collector
참조되지 않는 객체를 수집하고 제거한다.
(GC에 대한 내용은 다음 포스팅에서 작성)