JVM도 하나의 애플리케이션이라고 할 수 있습니다.
자바 가상 머신(JVM)의 메모리 구조(Runtime Data Area)는 크게 다음과 같은 영역으로 나뉩니다.
- 메소드 영역(Method Area)
- 힙 영역 (Heap)
- 스택 영역 (Stacks)
- PC 레지스터 (PC Registers)
- 네이티브 메소드 스택 (Native Method Stacks)
- Runtime Data Area
✅ 메소드 영역(Method Area)
메서드 영역은 JVM의 메모리 구조 중 하나로, JVM이 시작될 때 생성됩니다.
메소드 영역은 클래스의 메타데이터 즉, 클래스 레벨의 정보들을 저장하는 공간입니다.
클래스 레벨의 정보는 다음과 같습니다.
- 클래스 이름
- 수퍼클래스 이름
- 인터페이스 코드
- 메소드의 바이트 코드
- 런타임 상수 풀
- 필드와 메서드 데이터
- 생성자
- 메소드 정보
이러한 메타데이터 중에서 메소드 정보 부분은 해당 클래스에 정의된 모든 메소드에 대한 메타데이터를 포함하며, 이는 vtable을 구성하는 데 사용됩니다.
🔹객체의 메소드는 메모리(Method Area)에 저장된 메소드 코드를 참조하고 있나요?
👉 네, 자바에서 객체의 메소드는 실제로 메소드 코드를 가리키는 메소드 영역에 저장됩니다. 자바에서 클래스는 메소드를 정의하고, 객체는 클래스의 인스턴스입니다. 클래스의 메소드는 해당 클래스의 행위나 동작을 정의하며, 메소드는 클래스의 멤버로 선언됩니다. 객체는 이러한 메소드를 호출하여 원하는 작업을 수행할 수 있습니다.
메소드 코드는 클래스의 정의 부분에서 작성되며, 해당 코드는 메소드 영역에 저장됩니다.
실제로 객체를 생성하고 객체 메소드를 호출할 때, 메소드의 코드가 메모리의 힙 영역에 위치한 객체의 인스턴스와 연결되어 실행됩니다. 즉, 힙 영역에 위치한 객체는 메소드를 직접 가지고 있지 않고 Method Area에 저장되어 있는 메소드와 맵핑되어 실행되게 됩니다.
따라서 자바에서 객체의 메소드는 실제로 메소드 코드를 가리키는 것으로 이해할 수 있습니다. 이를 통해 객체의 메소드가 실행되면, 해당 메소드의 코드가 실제로 실행되어 원하는 작업을 수행하게 됩니다.
메소드 테이블은 클래스에 정의된 모든 메소드의 주소(메소드 코드를 가리키는 포인터)를 저장하는 테이블입니다. 이 테이블은 상속 관계에 있는 클래스들 간에 공유되며, 각 클래스의 메소드에 대한 정보를 담고 있습니다.
객체의 인스턴스는 해당 클래스의 메소드 테이블을 가리키는 포인터를 가지고 있습니다. 이 포인터는 객체가 어떤 클래스의 인스턴스인지를 가리키고, 해당 클래스의 메소드 테이블(vtable)을 참조할 수 있게 합니다. 따라서 객체가 메소드를 호출할 때, 해당 메소드의 코드를 직접 가리키는 것이 아니라, 메소드 테이블을 통해 해당 메소드의 주소를 찾아서 실행합니다.
이 방식은 다형성(polymorphism)을 지원하기 위한 메커니즘입니다. 상위 클래스 타입의 변수로 하위 클래스의 객체를 참조할 수 있는데, 이 경우 변수의 타입에 따라 호출되는 메소드가 달라지게 됩니다. 변수가 실제로 참조하는 객체의 타입이 무엇이냐에 따라 실행 시에 적절한 메소드가 호출되는 것입니다. 메소드 테이블은 이러한 동적 디스패치(dynamic dispatch)를 가능하게 하여 실행 시에 적절한 메소드를 호출할 수 있도록 합니다.
✅ 힙 영역(Heap)
JVM의 힙 영역은 OS가 프로세스에 할당한 메모리 공간에서 생성됩니다.
JVM의 힙 영역은 자바 애플리케이션에서 사용하는 모든 객체와 배열이 할당되는 공간입니다. JVM이 시작될 때 생성되며, 가비지 컬렉션에 의해 자동으로 관리됩니다. 이 힙 영역의 크기는 시작 시에 정의되지만, 프로그램 실행 중에 필요에 따라 늘어나거나 줄어들 수 있습니다. 힙 영역의 메모리는 더 이상 참조되지 않는 객체를 가비지 컬렉터가 정리함으로써 재사용할 수 있습니다.
Heap 영역은 모든 스레드에서 공유되는 메모리 영역이며, 스택과 비교할 때 힙 메모리의 크기가 더 크며 객체의 생성과 소멸은 동적으로 이루어집니다. 객체가 생성되면 Heap 영역에서 필요한 크기만큼 메모리를 할당받고, 객체의 참조는 스택이나 다른 Heap 영역의 객체에 의해 관리됩니다. 객체의 참조는 변수를 통해 관리되는데 이는 스택(Stack) 영역에 메소드 호출과 관련된 데이터로 저장됩니다. 변수에는 생성된 객체의 메모리 주소가 저장되며, 이를 통해 해당 객체를 참조할 수 있게 됩니다.
스택 영역에 저장된 변수는 일시적인 유효 범위를 가지고 있으며, 변수의 유효 범위를 벗어나면 해당 변수는 스택에서 제거됩니다. 그러나 객체는 스택 영역의 변수가 아닌 Heap 영역에 메모리를 할당받아 저장되며, 객체가 소멸되는 시점은 더 이상 해당 객체를 참조하는 변수가 없을 때입니다.
요약하자면, 스택 영역에 변수나, 힙 영역에 저장된 객체가 할당 받은 메모리를 통해서 객체 참조가 이루어지게 됩니다.
✅ 스택 영역(Stack)
JVM의 자바 스택 영역은 특정 스레드가 메서드를 실행하는 동안 사용되는 데이터를 저장하는 메모리 영역입니다. JVM의 메모리 구조 중 하나인 스택(Stack) 영역은 스레드마다 별도로 생성되며, 메소드 호출과 관련된 정보를 저장합니다. 스택은 메소드의 호출과 반환에 따른 데이터와 실행 정보를 관리하며, 메소드 호출 정보, 지역 변수, 매개 변수, 리턴 값, 임시 데이터 등을 저장합니다.
스택은 LIFO(Last-In-First-Out, 후입선출) 방식으로 데이터를 저장합니다. 스레드가 메서드를 호출할 때마다 그 메서드를 위한 새로운 스택 프레임(Frame)이 스택의 상단에 푸시(push)되고, 해당 메서드의 실행이 완료되면 그 스택 프레임은 팝(pop)되어 제거됩니다.
- 스택 프레임(Stack Frame) : 일반적인 의미
스택 프레임은 메소드의 실행에 필요한 정보를 저장하는 작은 블록입니다. 각 프레임은 로컬 변수(Local Variables) 공간, 연산 스택(Operand Stack), 메소드의 리턴 주소 등의 정보를 포함합니다.
- 로컬 변수 배열
메서드 내에서 선언된 모든 변수가 이 배열에 저장됩니다. 각 변수는 인덱스를 통해 접근할 수 있으며, 메서드의 실행이 종료되면 해당 메서드의 로컬 변수도 함께 사라집니다.
- 연산 스택
메서드가 실행되는 동안 일시적인(중간) 계산 결과를 보관하는 데 사용됩니다. 예를 들어, 두 개의 변수를 더하려면 두 변수를 스택에 푸시하고, 두 값을 팝하여 더한 다음 결과를 다시 스택에 푸시합니다.
- 프레임 데이터
메서드가 반환될 때 제어가 전달되는 위치(return address)나 "동적 링크(dynamic link)"와 같은 추가 정보가 포함됩니다. 동적 링크는 이전 스택 프레임에 대한 참조입니다.
자바 스택은 메서드 호출과 메서드 실행에 필요한 메모리를 관리하는데 사용되며, 각 스택 프레임의 생명주기는 해당 메서드의 생명주기와 일치합니다. 메서드가 종료되면 해당 스택 프레임도 스택에서 제거되므로, 스택에서 메모리를 명시적으로 해제할 필요가 없습니다. 이는 가비지 컬렉션과는 별개의 자동 메모리 관리 메커니즘입니다.
- 로컬 변수 배열
✅ PC 레지스터(Program Counter Register)
PC 레지스터(Program Counter Register)는 각 스레드가 생성될 때마다 생성되는 스레드 당 프라이빗 메모리 영역입니다. 실제 CPU의 레지스터를 의미하는 것은 아닙니다.
PC 레지스터는 다음 클럭에 실행될 JVM 명령어의 메모리 주소를 가지고 있는 공간입니다. 프로그램 카운터의 값은 명령어가 순차적으로 실행되는 동안 증가하며, 제어 흐름 명령어(예: 조건부 브랜치, 루프, 메서드 호출 등)가 실행될 때마다 업데이트됩니다.
CPU의 레지스터가 OS의 의해 10ms의 점유 시간이 종료되었을 때, 스레드 간에 문맥 교환(context switching)이 발생하며, 이때 PC 레지스터가 동작합니다. 문맥 교환은 실행 중인 스레드가 다른 스레드로 변경되는 과정을 의미하고, 이때 PC 레지스터의 값이 각 스레드의 실행 상태를 저장하고 복원하는데 사용됩니다. 이는 CPU 레지스터의 context switching이 발생하고난 뒤 다음 점유 시간이 도달할 때 기존의 작업을 이어서 하기 위해 기록하는 내역인 “컨텍스트(Context)”과는 다른 개념입니다. Java 애플리케이션이 동작할 때 PC 레지스터는 CPU 레지스터의 “컨텍스트(Context)”와는 독립적으로 현재까지 사용된 JVM 명령어의 메모리 주소를 기록한 것입니다.
✅ 네이티브 메소드 스택(Native Method Stack)
네이티브 메소드 스택은 JVM의 메모리 구조 중 하나로, 자바 외부에서 작성된 네이티브 메소드를 실행하는 데 필요한 메모리를 제공합니다. 이 메모리 영역은 각 스레드에 대해 별도로 생성되며, 각 스레드는 자신만의 독립적인 네이티브 메소드 스택을 가집니다.
네이티브 메소드 스택은, 네이티브 메소드가 실행되는 동안 필요한 변수나 중간 결과 등을 저장하는 데 사용됩니다. 이 스택은 각 스레드가 네이티브 메소드를 호출할 때마다 생성되며, 메소드의 실행이 완료되면 스택은 제거됩니다.
🔹자바 네이티브 인터페이스(Java Native Interface, JNI)
JNI(Java Native Interface)은 자바 언어와 다른 네이티브 언어(예: C, C++) 간의 상호 운용성을 제공하기 위한 인터페이스입니다. JNI를 사용하면 자바 언어로 작성된 애플리케이션에서 네이티브 코드를 호출하거나 네이티브 코드에서 자바 언어의 기능을 사용할 수 있습니다.
JNI는 플랫폼(OS)에 종속적인 기술이므로, JNI를 사용하는 경우에는 해당 플랫폼에 맞는 네이티브 코드를 작성해야 합니다. 또한, JNI는 성능 저하를 가져올 수 있으므로 네이티브 코드를 사용해야 하는 경우에만 사용하는 것이 권장됩니다. JNI는 자바 플랫폼의 확장성과 유연성을 높여주는 중요한 도구로, 주로 시스템 레벨의 작업이 필요한 경우나 네이티브 라이브러리를 활용해야 하는 경우에 활용됩니다.
🏷️이미지 출처 및 참고한 사이트
Uploaded by N2T