Runtime Data Area의 구성
🎯 목표
🚀 JVM의 Runtime Data Area의 5가지 구성에 대해서 알아봅니다.
🚀 각 구성의 특징을 이해합니다.
🙊 JAVA의 정신으로 부터...
💡 자바는 "Write Once, Run Everywhere"라는 철학으로 만들어진 프로그래밍 언어입니다.
💡 즉, 플랫폼 독립성을 목표로한 언어입니다.
💡 자바에서는 이를 4가지 연관 기술을 통해 실현합니다.
- JAVA Programming Language
- JAVA Class File Format
- Java Application Programming Interface(Java API)
- Java Virtual Machine(JVM)
이 문서에서는 Java Virtual Machine, 그 안에서도 Runtime Data Area을 살펴보겠습니다.
Runtime Data Area
- Runtime Data Area는 JVM이 프로그램을 수행하기 위해 OS로부터 할당 받은 메모리 영역입니다.
- 세부 구현은 밴더마다 다를 수 있습니다.
- Rumtime Data Area는 Heap Area, Metaspace(MethodArea), JVM Stack, Native Method Stack, PC Register로 구성 됩니다.
1. Heap Area
🔎 정의
🔎 정의
-Instance, 배열이 저장되는 공간입니다.
🔎 특징
- JVM이 구동 시에 생성되고, 종료 시에 사라집니다.
- 모든 Thread에서 접근이 가능합니다.
- 동기화 이슈가 발생할 수 있습니다.
🔎 구조
Young Generation
👉 Eden : Object가 최초로 할당되는 영역입니다.
해당 영역이 꽉차면 Manor GC가 발생하는데 이때 살아있는 객체는 Survivor1영역으로 이동합니다.
죽은 객체는 남겨놨다가 모든 살아있는 객체가 Survivor1영역으로 이동하면 Eden영역을 비웁니다.
👉 Survivor1 & 2 : Eden에서 살아남은 객체가 머무는 영역입니다.
살아있는 객체를 옴길 때는 반드시 한 Survivor영역은 비워줘야합니다.
Old Generation
👉 Youn Generation에서 오래 살아남은 객체가 이동되는 곳 입니다.
이렇게 이동하는 것을 promotion이라 합니다.
오래 참조될 가능 성이 높은 객체를 모아두는 장소입니다.
* 메모리 구조 외에 GC의 자세한 메커니즘은 Garbage Callection파트에서 다루겠습니다.
2. Metaspace
🔎 정의
- Class Loader에 의해서 load된 Type(Class, Interface 등)정보, 상수 등을 저장하는 메모리 공간입니다.
🔎 특징
- JVM이 시작될 때 생성되며, GC 의 대상이 됩니다.
- 클래스가 로드될 때 할당되고, 클래스가 언로드될 때 GC에 의해서 해제됩니다.
(Interned String 포함)
- Hotspot JVM의 경우 Permanent Area라는 명칭으로 특정 메모리영역으로 구분되어 있습니다.
🔎 구성
(Interned String 포함)
load된 class는 아래 7개의 정보로 구성됩니다.
Type Information
타입(클래스나 인터페이스)의 전반적인 정보를 담습니다.
- Full Qualified Name
- SuperClass Full Qualified Name
- Class인지 Interface인지 여부
- Type의 접근지정자
Runtime Constant Pool
: class나 interface에 대한 모든 상수 정보를 가지고 있습니다.
이때 상수란 literal constant를 포함한 type, field, method로의 모든 참조를 말합니다.
-
하나의 클래스마다 하나의 상수 풀을 가집니다
-
상수풀은 1~n으로 넘버링 되어있으며 타입, 값에 대한 정보를 담고 있습니다.
-
아래 바이트 코드 참고해봅시다.
public class org.jvminternals.SimpleClass SourceFile: "SimpleClass.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER //상수풀!!! 이 부분을 보세요! Constant pool: #1 = Methodref #6.#17 // java/lang/Object."<init>":()V #2 = Fieldref #18.#19 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #20 // "Hello" #4 = Methodref #21.#22 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #23 // org/jvminternals/SimpleClass #6 = Class #24 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lorg/jvminternals/SimpleClass; #14 = Utf8 sayHello #15 = Utf8 SourceFile #16 = Utf8 SimpleClass.java #17 = NameAndType #7:#8 // "<init>":()V #18 = Class #25 // java/lang/System #19 = NameAndType #26:#27 // out:Ljava/io/PrintStream; #20 = Utf8 Hello #21 = Class #28 // java/io/PrintStream #22 = NameAndType #29:#30 // println:(Ljava/lang/String;)V #23 = Utf8 org/jvminternals/SimpleClass #24 = Utf8 java/lang/Object #25 = Utf8 java/lang/System #26 = Utf8 out #27 = Utf8 Ljava/io/PrintStream; #28 = Utf8 java/io/PrintStream #29 = Utf8 println #30 = Utf8 (Ljava/lang/String;)V { public org.jvminternals.SimpleClass(); Signature: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lorg/jvminternals/SimpleClass; public void sayHello(); Signature: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String "Hello" 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 6: 0 line 7: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lorg/jvminternals/SimpleClass; }
-
위의 sayHello 메소드의 바이트코드 3번 라인을 보면 ldc #3 라는 Instruction이 있습니다.
-
이는 상수풀의 3번째 데이터를 로드 하라는 의미고 옆에 그 데이터가 hello라는 String 데이터라 주석을 달아줍니다.
-
위로 올라가 상수풀을 직접보면 해당 데이터가 무엇인지 알 수 있습니다.
Field Information
: class에서 선언된 모든 field 정보가 있습니다.
- field name
- field data type / 선언순서
- field의 접근지정자.
Method Information
: class에서 선언된 method의 정보가 저장되는 영역입니다.
- method name
- method return type or void
- method parameter의 수, data type, 선언 순서
- method 접근지정자
Class Variable
: class 안에서 static 으로 선언된 변수가 저장 되는 영역입니다.
- 모든 instance에서 공유됩니다.
- 동기화 이슈가 발생합니다.
- instance없이 접근 가능합니다.
- final을 붙이면 constant pool에 저장됩니다.
Reference to class Lcass Loader
: class가 JVM에 의해 load될 때 어떤 Class Loader에 의해서 load되었는지 정보가 저장 되어있습니다.
Reference to Class class
: class가 JVM에 의해서 load되면 항상 java.lang.class 클래스의 instance가 생성됩니다.
- method area에서는 type 정보의 일부로 이 instance를 Reference 합니다.
Method Table
- Java는 수 많은 Reference 타입들을 이용하는 언어로 이 reference들을 잘 찾아다닐 수 있도록 하는 것이 중요합니다.
- 이를 위해서 method table이라는 데이터 구조를 사용함
- Method Table은 class의 method에 대한 Direct Reference를 갖는 자료구조입니다.
- 부모 클래스의 메소드에 대한 참조도 가지고 있습니다.
🔎 Java 8이후
- Java 8 부터는 Metaspace로 명칭이 바뀌고 Native Memory에서 OS에 의해 관리됩니다.
- Static Object들은(Interned String 포함) Heap에서 관리하고 GC대상이 됩니다.
- Native Memory는 OS에 의해 자동으로 크기가 관리됩니다.
3. JVM Stack
🔎 정의
- 각 Thead의 Stack Frame을 저장하는 메모리 영역입니다.
🔎 특징
- Thread가 생성될 때 같이 생성됩니다.
- Thread가 마다 하나씩 생성 됩니다.
- Stack Frame들로 구성 됩니다.
- JVM Stack은 Stack Frame을 push하거나 pop하는 작업만 수행합니다.
- Stack 자료구조와 동일하게 작동합니다.(linear, Last in- First out)
🔎 Stack Frame
: Thread에서 method를 수행 할 때 수행정보를 저장하는 공간입니다.
이 수행 정보는 Local Variable Section, Operand Stack, Frame Data로 구성됩니다.
🔎 Stack Frame 동작
- Thread가 Method를 수행하면 Stack Frame을 생성하여 JVM Stack에 push 한다.
- 컴파일 타임에 사이즈가 정해진다.
- 새로 들어간 Stack Frame이 Current Frame이 된다.
- 해당 Method가 수행을 마치면 Current Frame은 pop된다.
- pop 이후 이전 Stack Frame이 Current Frame이 된다.
- Method 수행 중 Exception이 발생해도 Stack Frame에서 pop된다.
🔎 Stack Frame 구성
Local Variable Section
: 수행하는 Method의 Parameter Variable, Local Variable들을 저장하는 공간입니다.
- Local Variable Section은 배열이다. 0부터 할당되며 index로 접근한다
- instance method는 0에 this를 할당하고, static method는 0부터 Parameter Variable을 할당한다.
- Parameter Variable 순서대로 할당되며, Local Variable은 컴파일러가 알아서 할당한다
- Local Variable은 컴파일러가 최적화 하여 할당 할수도 안할 수도 있다
- Local Variable Section 컴파일 타임 Stack Frame이 사이즈가 정해지면서 같이 사이즈가 정해진다.
- 각 인덱스의 크기는 32bit이다.
- primetive 타입 중 long과 double은 두 칸을 차지하고 나머지는 1칸씩 차지한다
- byte, char, short Stack Frame 에는 int형으로 변환하여 저장되고, Heap Area에서는 원래형으로 복원된다.
- boolean도 Stack Frame에서 int 형으로 변환되지만, Heap Area에서 다시 원래 타입으로 복원되지 않는다.
- long과 double은 64bit의 크기를 가지며 Local Variable Section에서 두 칸을 차지하는데,
두 자료형에 접근할 때는 첫번째 칸의 인덱스로 접근한다.
- reference 타입은 주소값이 할당되며 1칸을 차지한다.
Operand Stack
:피연산자 스택, JVM이 Local Variable Section에서 데이터를 가져와 계산할 때 사용하는 작업공간입니다.
👉 특징
- Stack 자료구조를 갖는다.
- Local Variable Section에서 값을 받아오거나, 연산한 결과를 돌려주는 역할을 한다.
👉 연산 방식
- Instruction을 실행하기 위해서 [[Local Variable Section]]에서 값을 복사하여 Operand Stack에 push한다.
- push된 값을 모두 pop하여 연산을 한다.
- 연산 후 Operand Stack에 push한다.
- push된 연산결과를 pop하여 [[Local Variable Section]]에 넣는다.
Frame Data
: Stack Frame의 정보를 저장하는 영역입니다.
👉 구성
- Constant Pool Resolution 정보(상수풀에 대한 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경)
- Method 정상 종료 정보(자신 스택프레임을 호출한 상위 스택프레임이 어딘지)
- Method 비정상 종료 시 Exception 관련 정보(예외 발생 시 처리해야할 정보)
👉 특징
- Constant Pool Resolution을 통한 Contant Pool 접근
- Stack Frame은 method가 실행되면서 생성됩니다.
- 현재 실행 중인 method의 정보를 확인할 수 있고, 해당 method가 속한 class의 상수풀을 참조할 수 있습니다.
- Constant Pool Resolution은 현재 클래스의 Contant Pool에 접근할 수 있는 포인터 정보입니다.- 자신을 호출한 Stack Frame의 Instruction Pointer를 가지고있습니다.
- Method가 종료되면 JVM은 이 정보를 PC Register에 설정하고 Stack Frame을 빠져나옵다.
- 만약 return 값이 있으면 자신을 소출한 method의 Stack Frame의 Operand Stack에 push합니다.- Method의 비정상 종료에 대한 정보를 담고 있습니다.
- Exception Table의 Reference를 가지고 있습니다.
- 각 class 파일은 Exception Table을 가집니다.
- Exception이 발생하면 jvm은 Exception Table 참조하여 catch절에 해당하는 bytecode로 점프합니다.
Exception Table
from | to | target | type |
---|---|---|---|
5 | 9 | 12 | class java/lang/NullPointException |
- from은 try 절이 시작되는 bytecode의 라인 넘버를 의미합니다
- to는 try 절이 끝나는 bytecode의 라인 넘버를 의미합니다
- target은 try절에서 Exception이 발생 했을 때 점프해야할 bytecode의 라인 넘버를 의미합니다
- type은 catch에서 정의한 exception을 의미합니다
- exception이 발생하면 type의 클래스 정보와 비교하여 target으로 점프할지 여부를 정함.
비일치 시 Current Frame을 종료 후 이를 호출한 Method의 Stack Frame에 Exception을 다시 던져 위 과정을 반복합니다다.
4. Native Method Stack
🔎 정의
- Native Code로 되어있는 Function을 호출할 때 생성되는 메모리 영역입니다
- JVM은 Native Method를 Java Native Interface라는 표준 규약을 통해 제공됩니다.
🔎 특징
- JVM Stack과 동일하게 Stack 자료구조입니다.
- Native 언어에 맞게 생성됨. C로 작성되면 C Stack, C++로 작성되어있으면 C++ Stack이 생성됩니다.
🔎 JVM Stack에서 Native Function을 호출
- Java metod 1를 호출하면 StackFrame 1 push.
- StackFrame 1에서 Native Function을 호출하면 StackFrame 2가 push됨
- 그리고 Native Method Stack에도 Stack Frame이 push됨
- Native Method Stack의 Stack Frame이 작업을 수행함
- Native Function의 작업이 끝나면 Thread는 JVM Stack에 StackFrame 3 을 새로 생성하고 이곳으로 돌아옴. (Native Function을 호출했던 StackFrame 2로 돌아가지 않음)
5. PC Register
🔎 사전 지식
- CPU 는 Regiser - Base로 명령어(Instruction)을 처리합니다
- 데이터의 최소단위는 피연산자(Operand), 연산자(Operator)입니다.
- 이 둘을 합친 것이 명령어(Instruction)입니다.
- Instruction을 처리할 때 Operand를 저장할 공간이 필요합니다. 이것이 Register입니다.
- 하지만 Java는 Stack - Base로 명령어를 처리합니다.
- Java는 플랫폼 독립적이므로 CPU와 그안의 Register를 직접 사용하지 않습니다.
🔎 정의
- 현재 실행중인 Thread의 Instruction 주소를 저장하는 공간입니다.
🔎 특징
- Thread가 생성될 때 같이 생성 됩니다.
- 각 Thread마다 하나씩 생성 됩니다.
- Thread가 Java Method를 수행하면 PC Register는 이 수행 중인 JVM Instruction의 주소를 가지고있습니다.
- 만약 Native Method를 수행하면 PC Register는 undefined 상태가 됩니다.
- 왜냐하면 Native Method는 플랫폼 종속적이므로 JVM을 거치고 실행되지 않습니다.
🔎 필요이유
- 자바는 멀티 스레드 언어 입니다.
- Context Switching에 필요한 정보를 담습니다.
- Thread가 번갈아가며 실행 될 때 해당 Thread의 실행 정보를 담고 있어야 다시 돌아올와 다시 명령어를 수행 할 수 있습니다.
Runtime Data Areas Simulation
Java Variable Arrangement
Simulation
참조:
Java Performance Fundamental - 김한도 저
Author And Source
이 문제에 관하여(Runtime Data Area의 구성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@indongcha/Runtime-Data-Area의-구성
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
- Java metod 1를 호출하면 StackFrame 1 push.
- StackFrame 1에서 Native Function을 호출하면 StackFrame 2가 push됨
- 그리고 Native Method Stack에도 Stack Frame이 push됨
- Native Method Stack의 Stack Frame이 작업을 수행함
- Native Function의 작업이 끝나면 Thread는 JVM Stack에 StackFrame 3 을 새로 생성하고 이곳으로 돌아옴. (Native Function을 호출했던 StackFrame 2로 돌아가지 않음)
🔎 사전 지식
- CPU 는 Regiser - Base로 명령어(Instruction)을 처리합니다
- 데이터의 최소단위는 피연산자(Operand), 연산자(Operator)입니다.
- 이 둘을 합친 것이 명령어(Instruction)입니다.
- Instruction을 처리할 때 Operand를 저장할 공간이 필요합니다. 이것이 Register입니다.
- 하지만 Java는 Stack - Base로 명령어를 처리합니다.
- Java는 플랫폼 독립적이므로 CPU와 그안의 Register를 직접 사용하지 않습니다.
🔎 정의
- 현재 실행중인 Thread의 Instruction 주소를 저장하는 공간입니다.
🔎 특징
- Thread가 생성될 때 같이 생성 됩니다.
- 각 Thread마다 하나씩 생성 됩니다.
- Thread가 Java Method를 수행하면 PC Register는 이 수행 중인 JVM Instruction의 주소를 가지고있습니다.
- 만약 Native Method를 수행하면 PC Register는 undefined 상태가 됩니다.
- 왜냐하면 Native Method는 플랫폼 종속적이므로 JVM을 거치고 실행되지 않습니다.
🔎 필요이유
- 자바는 멀티 스레드 언어 입니다.
- Context Switching에 필요한 정보를 담습니다.
- Thread가 번갈아가며 실행 될 때 해당 Thread의 실행 정보를 담고 있어야 다시 돌아올와 다시 명령어를 수행 할 수 있습니다.
Runtime Data Areas Simulation
Java Variable Arrangement
Simulation
참조:
Java Performance Fundamental - 김한도 저
Author And Source
이 문제에 관하여(Runtime Data Area의 구성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@indongcha/Runtime-Data-Area의-구성
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
참조:
Java Performance Fundamental - 김한도 저
Author And Source
이 문제에 관하여(Runtime Data Area의 구성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@indongcha/Runtime-Data-Area의-구성저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)