Programming/Java

JVM의 메모리 구조는 어떻게 될까?

KOOCCI 2022. 8. 15. 21:42

목표 : JVM 메모리 구조를 설명할 수 있다.


JVMjava Virtual Machine 이라는 것은 잘 알고 있다.

다만, 이 구조에 대해 설명을 하려고 하면, 쉽지 않은 부분이 있어 정리하고자 한다.

JVM,JRE,JDK

왜 자바가 많이 사용되었을까?

근본적인 질문일 수 있다.

JVM을 설명하기 전에, Java의 특징 중 하나인 OS에 종속적이지 않다는 점을 설명하기 위한 질문이다.

그럼, OS에 어떻게 종속받지 않은 환경을 만들었을까?

어디에서든 Java가 돌아갈 수 있게 감쌀 무언가가 필요했고, 그게 바로 Java Virtual Machine 이라고 하는 JVM이다.

 

Java는 컴파일 언어다.

Java의 특징 중 또 다른 하나는 컴파일 언어라는 것이다.

컴파일 언어라 함은, 기계어로 번역하는 과정이 필요하며, 이에 따라 빌드 시간이 다소 소요되지만, 미리 컴파일 에러를 잡아낼 수 있고, 런타임 상황에서 빠른 실행이 가능하다.

 

Java는 컴파일 언어로서, 기계어로 번역하는 과정이 필요하다.

우리가 처음 만든 .java 파일을 Bytecode로 변경하는 작업이 필요한 것이다.

 

즉, java compiler가 (JDK(Java Development Kit) 의 javac) .java 파일을 .class라는 파일로 변환하게 된다.

 

Java Bytecode to Binary Code

.java 파일이 컴파일러에 의해 .class파일로 변경되며, Java Bytecode가 된다는 사실을 알게 되었다.

Java BytecodeJVM(가상 머신)이 이해할 수 있는 언어로 변환된 코드다.

 

JVM이라는 가상환경이 Java와 OS간의 연결을 도와주는 매개체라고 생각해보자.

 

이제 JVM은 OS가 이해할 수 있는 언어로 변환하는 과정이 필요하다.

OS가 이해하는 언어는 Binary Code 즉 이진 코드이다. (CPU가 이해하는 언어)

 

JIT Compiler

Bytecode를 Binary Code로 바꾸려면, 또다시 변환과정이 필요하게 된다.

이전에는 RunTime시에 Interpreter로 적용되어 속도가 느렸지만, 요즘은 그 단점을 해소하기 위해 JIT(Just In Time) Compiler가 도입되어 있다.

JIT(Just In Time) Compiler 는 인터프리터 방식으로 실행하다 적절한 시점에 바이트코드 전체를 네이티브 코드(ex> 바이너리 코드)로 바꾼다. 그 다음부터 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용한다. 즉, 한번 컴파일된 코드는 계속 빠르게 수행되게 된다.

JIT Compiler

그림으로 이해해보자.

이제 우리가 만든 Java 파일이 어떻게 변환되어 OS까지 전달되는지 알수 있게 되었다.

도식도를 보고 한번 더 이해해보도록 하자.

JVM 구성

클래스 로더 (Class Loader)

  • JVM 내에 초기화, 클래스 파일(.class)을 로드하고, 링크를 통해 배치한다.
  • .class 파일을 읽고 그 내용에 따라 바이너리 데이터를 만들어 Method 영역에 저장한다. (메모리 구조는 다음 탭에서 보자)

실행 엔진 (Excution Engine)

  • 앞서 알아본 바이너리 코드로 바꾸기 위한 Interpreter나 JIT Compiler가 존재한다.
  • 또한, GC가 있어, 힙 메모리 영역에 생성된 객체들 중에서 참조되지 않은 객체들을 탐색하고 제거한다.

 

JVM의 메모리는 어떻게 구성되어 있을까?

JVM에서는 프로그램을 수행하기 위해 OS로부터 메모리 공간들을 할당 받는다. (즉 하나의 프로세스다)

그 공간들을 의미하는 Runtime Data Area에 대해 알아보기 위해 구체화된 다음 이미지를 보도록 하자.

 

Runtime Data Area 1
Runtime Data Area 2
Runtime Data Area 3

 

JVM이라는 프로세스

Runtime Data Area 4

먼저, 앞서 JVM이 하나의 프로세스라고 하였다.

Main Thread를 가진 독립된 메모리 공간을 가지며, Runtime Data Area 3 이미지 처럼 구성된다.

JVM 내에 쓰레드가 구성될 때는 크게 Main Thread(비데몬 스레드)와 Main이 아닌 Thread(데몬 쓰레드)로 구분된다.

 

static void main(String[] args) 가 프로그램 실행의 진입점인 것을 알고 있다. 즉, 이 진입점이 비데몬 쓰레드, Main Thread로 실행되며 Main Thread가 종료되면, 다른 데몬 쓰레드도 모두 종료된다.

 

PC(Program Counter) Register

 

OS에서 Context Switching에 대해 학습했다면, PC라는 용어를 알 것이다.

실행중인 프로세스(쓰레드)에서 다른 프로세스(쓰레드)로 넘어가 실행되기 원할 때, Context Switching이 발생하며, 이때 그 위치에 대한 정보를 포인터하는 역할을 PC가 하게된다.

즉, 현재 쓰레드의 명령 흐름을 추적하거나, 연산하는 과정에서 필요한 데이터를 저장하는 역할을 한다.

 

Native Method Stack

Java가 아닌 다른 언어를 JNI(Java Native Interface)를 통해 실행하기 위한 코드 공간이다. (C, C++)

 

JVM Stack

우리가 흔히 말하는 Stack 공간이다.

지역 변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값등이 생성되는 영역으로, 프로그램 실행과정에서 임시로 할당되었다가 메소드를 빠져나가면 바로 소멸되는 특성을 가진다.

위 이미지에서 보듯, Thread에서도 자신만의 고유 공간이다.

 

Method Area

Method Area, 메소드 영역은, Class Area, Static Area, Code Area라고도 한다.

그림과 같이, 모든 쓰레드가 공유하는 메모리 영역이기도 하다.

클래스 로더의 설명에서도 나왔지만, 바이너리 코드로 변환된 클래스 파일들을 저장하는 공간이다.

클래스 변수(Static 변수), 인터페이스, 필드, 메소드 등 클래스 정보들이 저장된다.

 

또한, Runtime Constant Pool 이라는 중요한 저장 공간이 내부에 있다.

이는 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 수행한다.

int a = 1;
int b = 1;
if(a == b) {
    System.out.println("a == b");
} else {
    System.out.println("a != b");
}

// a == b

간단한 소스를 보자.

위의 a와 b 변수에 할당된 1이라는 값은 Constant pool에 저장된다.

따라서, 위 두 값은 같다라는 수식에서 같다라는 걸 알 수 있다.

Integer a = new Integer(1);
Integer b = new Integer(1);
if(a == b) {
    System.out.println("a == b");
} else {
    System.out.println("a != b");
}

// a != b

new로 생성한 값은 Constant pool에 저장되지 않아, 같다라는 조건이 성립되지 않는다.

 

Heap

힙(heap) 영역은 자바 프로그램에서 사용되는 모든 인스턴스 변수가 저장되는 영역이다.

new 연산자로 생성되는 객체와 배열을 저장하는 공간으로 보면 되며, Method Area(Class Area)에 올라온 클래스들만 객체로 생성할 수 있다.

 

Heap은 또 다시 내부 구조를 가진다.이는 GC와 연결되므로, GC를 따로 공부하면서 정리하도록 하자.

 

Wrap Up

위와 같이 JVM의 구조에 대해 알아보았다.정리하면 다음과 같다.

 

  • JVM은 Java가 OS에 종속적이지 않도록 만들어 준다.
  • Java는 컴파일 언어로, Javac를 통해 .class 파일로 변경된다 (바이트 코드)
  • .class 파일은 JVM 내부에서 Interpreter와 JIT Compiler를 통해 바이너리 코드로 변환되어 실행된다.
  • JVM 내부에는 크게, 클래스 로더, 실행 엔진, Runtime Data Area(메모리)로 나누어진다.
  • 클래스 로더는 .class 파일을 로드하고, 메모리(Method Area)에 저장하는 역할을 한다.
  • 실행 엔진에는 바이너리 코드로 변경하기 위한 Interpreter, JIT Compiler, 그리고 Heap 메모리의 GC 가 포함되어 있다.
  • Runtime Data Area에는 JVM Stack, PC Register, Native Method Stack과 Method Area, Heap Area이 존재한다. 
  • JVM Stack 은 Thread 고유 저장 영역으로 지역 변수, 파라미터, 리턴 값들이 저장되었다가, 메소드가 끝나면 해소되는 휘발성 영역이다.
  • Method Area는 모든 Thread이 공유하는 영역으로, 바이너리 코드로 변환된 .class 내용이 저장된다. 즉, 클래스 변수, 필드, 메서드 등 클래스 정보가 저장되는 공간이다.
  • Runtime Constant Pool도 가지고 있어, Primitive Type의 변수들에게 할당되는 값들이 저장되는 공간도 존재한다.
  • Heap 영역은 new 로 생성된 인스턴스 변수가 할당된다. 

참고자료

https://steady-coding.tistory.com/305

 

JVM 메모리 구조란? (JAVA)

안녕하세요? 코딩 중독입니다. 오늘은 JVM 메모리 구조에 대해 알아보겠습니다. JVM이란? JVM 메모리 구조를 설명하기 전에 JVM이 무엇인지 알아야 합니다. JVM은 Java Virtual Machine의 약자로, 자바 가상

steady-coding.tistory.com

https://catsbi.oopy.io/df0df290-9188-45c1-b056-b8fe032d88ca

 

[1주차] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

목표

catsbi.oopy.io

https://doozi0316.tistory.com/entry/1%EC%A3%BC%EC%B0%A8-JVM%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B8%EA%B0%80

 

[JAVA] JVM이란? 개념 및 구조 (JDK, JRE, JIT, 가비지 콜렉터...)

JVM이란 무엇인가 Java Virtual Machine의 줄임말. 직역하면 '자바를 실행하기 위한 가상 기계(컴퓨터)'라고 할 수 있다. Java 는 OS에 종속적이지 않다는 특징을 가지고 있다. OS에 종속받지 않고 실행되

doozi0316.tistory.com