책을 읽고 Runtime Data Area 관련 내용을 정리했다. GC와 클래스 로더 관련 내용도 필기로는 정리했으나 내용이 너무 많아 다음 기회에^^
Wora를 반영한 Java
Java는
- Wora의 철학을 담은 프로그래밍 언어이다.
- Wora를 구현하기 위해 플랫폼 독립성을 가진 JVM을 고안했다.
Symbolic Reference
- 참조하고자 하는 대상(객체)의 특정 메모리 번지가 아닌, 대상의 이름 만으로 참조 관계를 구성한 것을 의미한다.
- class file이 JVM에 올라가게 되면, Dynamic Linking을 통해 Symbolic Reference를 Direct Reference로 변경하게 된다. (Runtime Constant Pool, Symbol table의 일종)
Network Byte Order
- intel은 Little Endian(큰 메모리 주소 → 작은 메모리 주소), Unix는 Big Endian(작은 메모리 주소 → 큰 메모리 주소)를 사용하는데, 자바는 Network로 데이터 전송 시 Big Endian을 사용한다.
Java API(Runtime Library)
- Java의 기본 api는 플랫폼 상관없이 실행되도록 하는 Wora가 반영되었다.
- 시나리오로 보기 - classfile에서 java.io.InputStream을 사용한다면
- class 파일 내의 Symbolic Reference를 이용하여 런타임 시 생성된 해당 인스턴스(java.io.InputStream, Java Api)로 접근한다.
- 실제 file에 대한 접근은 Native Method를 통해 OS에 명령을 전달해야 한다.
- OS에 Native Method가 실행되면, OS는 File io를 실행하고, io에 대한 결과를 다시 Native Method를 거쳐 Java Api로 전달한다.
Runtime Data Area
- 각 스레드가 독립적으로 관리하는 영역인 PC Registers, Stacks, Native Stack 과, 모든 스레드가 공유하는 영역인 Method Area, Heap Area로 구성되어 있다.
PC Registers
- 기본적으로 모든 프로세스들은 CPU에 Instruction을 전달하고, CPU는 Instruction을 수행하며 필요한 데이터(Operand)와 연산 명령(add 등)을 Register에 저장한다.
- 그런데 각 벤더의 CPU 별로 이 Register는 다르게 구성될 수 있다. Java는 Wora를 반영하기 위해서, 이 Register 영역을 JVM 내의 buffer 형식으로 두기로 했다. 이를 PC Registers라고 한다.
- CPU나 OS 입장에서 보면 JVM 또한 하나의 프로세스에 불과하므로, JVM은 현재 작업 내용을 CPU에 Instruction으로 제공해야 한다. PC Registers는 Operand가 포함된 Instruction의 주소를 가리키며, 이를 CPU에 Instruction으로 제공하게 된다. (Stack Based)
- Stack Based의 반대 개념으로 Register Based의 개념이 있으며, 이 둘의 차이는 Operand(피연산자)를 포인터로 제공하는지에 달려 있다. JVM은 기본적으로 PC Register를 통해 Operand의 포인터를 제공한다(위의 operand가 포함된 Instruction의 주소를 가리킨다는 말과 동일한 말이다).
- PC Registers에 저장되는 Instruction의 주소는 Native Pointer 일수도 있고, Method Bytecode의 시작값일 수도 있다.
JVM Stacks
- 메소드 호출 시 frame이 생성되고, 이는 호출한 스레드에 할당된 Stack 영역에 push, pop 기능을 통해 할당되고, 수행 완료 시 해제된다.
Stack Frame
- 스레드가 수행하고 있는 Application을 메소드 단위로 기록한 곳.
- 컴파일 시점에 class의 메타 정보를 통해 크기가 결정됨.
1) Local Variable section
- array로 구성, 0번 인덱스에는 클래스 인스턴스 참조, 이후 parameter variable과 Local variable로 구성됨. 인덱스로 데이터에 접근함.
- primitive type의 경우 고정된 크기로 변수가 할당된다.
- int(byte, short, char, boolean 타입 포함. Heap에서는 다시 원래의 형으로 원복하여 저장됨. boolean의 경우 어디서나 int 타입으로 저장된다.)
- long(2개의 entry 차지)
- float
- double(2개의 entry 차지)
- 객체의 경우 reference type으로 저장됨. 객체로 찾아가는 cpu 연산을 필요로 하고 해당 class의 인스턴스를 생성하는 것을 의미하므로 가능한 primitive type을 사용하는 것이 좋다.
2) Operand Stack
- 각 스레드가 연산을 위해 사용하는 데이터 및 그 결과를 넣고 처리하는 곳.
- Array로 되어 있으나 index를 사용하지는 않고, stack이라는 이름처럼 push, pop을 통해 데이터를 할당하고 처리한다.
3) Frame Data
- 메소드에 대한 Constant Pool Resolution 정보, 메서드 정상 종료 시 정보, 비정상 종료 시 Exception 정보가 저장된다.
*Constant Pool Resolution : Symbolic Reference가 Direct Reference로 변경되는 과정을 resolution이라 하며, 이는 Constant Pool에 기록된 Symbolic Reference들의 포인터 정보를 가져오는 것을 의미하므로 Constant Pool Resolution이라 한다.
- 메서드가 정상 종료 되면, stack에서 frame이 pop됨으로써 끝나게 되는데, frame data 에 포함된 Instruction Pointer를 통해 이 frame을 호출한 frame을 찾아 current frame을 삼는다. (PC Register에 기록된 Instruction Pointer 주소가 변경되는 것) 반환값이 있다면 current frame의 operand stack에 push하는 작업을 진행한다.
- 메서드가 비정상 종료되면(Exception 발생 시), 이에 따른 Exception 정보를 저장한 frame data를 찾아간다. 각 클래스의 Exception table에 접근하여 exception byte 코드를 실행한다. 만약 현재 프레임에서 exception에 대한 정보가 없다면, 현재 프레임을 종료하고 이 메소드를 호출한 메소드의 frame에 이 Exception을 다시 던지는 작업을 수행한다.
Native Method Stacks
- 자바가 Native method를 호출할 때 사용하는 스택
- 실제 벤더에서 이를 구현할 시 기존 메서드 스택과 구분하지 않는다.
Method Area
- Load된 Type을 저장하는 논리적 메모리 공간으로, 모든 스레드들이 공유하는 메모리 영역.
- GC의 대상이 됨.
- Type의 전체 이름(패키지+클래스명, Fully Qualified Name)
- Type의 직계 Super class의 전체 이름
- Type이 class인지 interface인지 여부
- modifer 정보 등
- field의 이름, 데이터 타입, 선언된 순서, 접근 제어자
- Method의 이름, 반환 값 데이터 타입, 파라미터, 접근 제어자
- native나 abstract가 아니면 Method의 bytecod, stack frame의 operand stack 및 Local variable section 크기, exception table이 추가됨.
4) Constant Pool
- 타입의 모든 Constant 정보를 가짐. → Literal constant, Type, Field(member, class), Method 의 모든 Symbolic Reference까지 확장한 개념
- 자바의 철학이 반영된 Symbolic Reference를 저장하는 역할을 한다.
- 자바는 객체를 참조할 때 코드 상에서 메모리 주소가 아닌, 객체의 이름(Symbol)을 참조한다. 이렇게 객체의 이름으로 reference를 구성하는 것이 Symbolic Reference.
- 자바는 실행 시 Constant Pool의 Symbolic Reference를 통해 메모리 주소(Direct Reference)를 동적으로 연결한다.
5) Class Variable
- class 사용 전부터 method area에 메모리 할당 받음.
- final로 선언 시 변수가 아닌 상수로 취급하여 Constant Pool에 Literal Constant로 저장됨.
6) Class Loader 참조
- 부트스트랩 클래스로더일 경우 null로 저장
- 동적 Linking 시, 해당 Type과 동일한 클래스로더를 통해 참조하는 Type을 로딩하기 위해 사용됨.
7) Method Table
- class의 method에 대한 Direct Reference를 갖는 자료구조로 Method Area에 저장되며, 해당 class의 method뿐 아니라 Super class에 의해 상속된 Method의 reference까지 포함된다. 클래스의 하위 자료이므로 class가 로드 될 때 함께 생성된다.
*Heap Area는 여기서 다루지 않음
참고
책 | Java Performance Fundamental, 김한도, 2009