본문 바로가기
Java

JVM 아키텍처

by wwns 2023. 10. 11.
반응형

JVM(Java Virtual Machine)과 VM(Virtual Machine)?

 

JVM이 자바가 구동되는 VM이라고 생각할 수 있는데, 성질이 VM과 동일한지, 같은 의미의 VM인지 궁금할 수 있습니다.

비슷한 질문이 스택오버플로우에 있었습니다 링크

Is the Java Virtual Machine really a virtual machine in the same sense as my VMWare or Parallels file?

 

해당 답변 중 핵심만 보면

The difference is essentially that the JVM is a virtualized processor and the other virtual machines are virtualized machines (including video card, network, and other external devices and hardware registers).

  • JVM은 가상화된 프로세서이고 다른 가상 시스템은 가상화된 시스템(하드웨어 장치를 포함하는)이라는 차이
  • The JVM is called a virtual machine because the JVM definition defines an abstract machine
    • JVM은 추상 시스템을 정의하기 때문에 JVM을 가상 시스템이라고 합니다

즉, VM은 물리적 하드웨어 시스템에 구축되어 자체 CPU, 메모리, 네트워크 인터페이스 및 스토리지를 갖추고 가상 컴퓨터 시스템으로 작동하는 가상 환경이고, 흔히 VM하면 들을 수 있는 하이퍼바이저는 하드 웨어에서 가상 머신의 리소스를 분리하고 적절히 프로비저닝 하여 VM에서 사용할 수 있도록하는 소프트웨어입니다.

 

정리하면, JVM(Ja​va Virtual Machine)은 프로세서(CPU)만 가상화하고 JRE(Java Runtime Environment)에서 Java 애플리케이션이 플랫폼 독립적이며 (운영 체제에 독립적으로) 어디서든 실행될 수 있도록 하는 역할을 합니다


JVM이 무엇이고 역할을 이해했으니 Architecture를 보며 동작하는 원리를 정리해보겠습니다

 

https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/

JVM의 Architectire는 크게 세 가지 구성요소로 나뉩니다

 

Class Loader

자바는 동적 로드, 즉 컴파일타임이 아니라 런타임에 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 특징이 있다. 이 동적 로드를 담당하는 부분이 JVM의 클래스 로더이다

  • .java 파일을 컴파일하면 자바 바이트 코드로 이루어진 .class 파일로 변환
  • 프로그램에서 이 클래스를 사용하려고 하면 클래스 로더가 메인 메모리에 로드한다
    • 메모리에 먼저 로드되는 클래스는 일반적으로 main()을 포함하는 클래스
  • 클래스 로딩 과정에는 Loading, Linking, Initialization 3단계가 있다

https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/

Loading

  • 로딩 (Loading): 클래스 또는 인터페이스의 바이트 코드를 가져와 해당 클래스 또는 인터페이스를 생성하는 프로세스를 로딩이라고 합니다
  •  Java에서는 세 가지 내장 클래스 로더가 제공됩니다 -> 계층 구조
    •  부트스트랩 클래스 로더
      • 최상위 클래스로더로 java.lang, java.net, java.util, java.io 같은 표준 Java 패키지를 로드
    • 익스텐션 클래스 로더
      • 부트스트랩 클래스 로더의 하위 클래스
      • 표준 Java 라이브러리(기본 자바 API)의 extensions를 로드
    • 애플리케이션 클래스 로더
      • 클래스 패스(classpath)에 있는 파일을 로드
      • 기본값은 애플리케이션의 현재 디렉토리로 설정
  • 클래스를 메모리로 로드하기 위해 클래스 로딩 메서드를 사용
    • ClassLoader.loadClass()
  • 클래스로더가 만약 클래스를 찾을 수 없을 때 자식 클래스 로더에 작업을 위임하는 방식(부트스트랩 -> 익스텐션 -> 애플리케이션) -> 위임 모델
    • 마지막까지 찾지 못하면 NoClassDefFoundError 혹은 ClassNotFoundException을 발생

Linking

로딩 과정을 마친 클래스는 메모리에 로드된 후 Linking 과정을 거칩니다

  • 검증(Verify)
    • `.class`파일에서 읽어 들인 클래스가 자바 언어 명세(Java Language Specification) 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사
  • 준비(Prepare)
    • 클래스나 인터페이스의 static 필드를 위한 메모리를 할당하고 기본값으로 초기화(클래스 파일에 선언된 초기값이 아닌 임시값 ex: boolean이라면 false)
  • 해결, 분석(Resolve)
    • symbolic references가 런타임 상수 풀(runtime constant pool)에 있는 direct references로 대체
    • 다른 클래스나 상수 변수에 대한 참조가 다른 클래스의 실제 참조로 해결되고 대체 된다
    • 상수풀(문자열, 정수, 클래스 및 인터페이스 참조..)
public class MyClass {
    public static final String MY_CONSTANT = "Hello, World!";
}
  • 위와 같은 클래스가 있다면 MY_CONSTANT가 문자열 상수이며 클래스의 상수 풀에 저장되는데, "Hello, World!"라는 문자열을  symbolic references로 참조하며 이를 direct references로 대체합니다
  • 런타임 시에 참조하게 되면 상수를 더 효율적으로 사용할 수 있으며, 문자열 등의 상수를 반복적으로 생성하지 않고 참조할 수 있음

Initialization

  • 클래스의 생성자를 호출하고 정적(static) 블록을 실행하며 모든 정적(static) 변수에 값을 할당하는 등의 작업을 수행
  • `clinit`이라는 메서드를 실행하여 클래스, 인터페이스의 초기화 메서드를 실행함 

Runtime Data Area

Method Area

메서드 영역은 모든 스레드가 공유하는 영역으로 JVM이 시작될 때 생성됩니다

  • JVM이 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메서드 정보, Static 변수, 메서드의 바이트코드 등을 보관
  • 메서드 영역은 JVM 시작 시 생성되며 JVM 당 하나의 메서드 영역이 있음
  • 모든 클래스의 공유 자원을 저장

Heap Area

힙 영역은 모든 객체와 객체에 해당하는 인스턴스 변수를 저장하는 공간

  • 인스턴스 또는 객체를 저장하는 공간으로 가비지 컬렉션 대상
  • 메서드 영역이나 힙 영역은 여러 스레드에 공유되기에 스레드 안전성을 고려해야 함

Stack Area

JVM에서 새로운 스레드가 생성될 때 마다 별도의 런타임 스택도 동시에 생성됩니다

  • JVM 내에서 메서드가 수행될 때마다 하나의 스택 프레임이 생성되어 해당 스레드의 JVM 스택에 추가되고 메서드가 종료되면 스택 프레임이 제거
    • 스택 프레임 구성 요소 - 로컬 변수, 오퍼랜드 스택, 프레임 데이터
  • JVM은 오직 JVM 스택에 스택 프레임을 추가하고(push) 제거하는(pop) 동작만 수행

PC Register

  • JVM에서 여러 스레드를 동시에 지원할 때 각 스레드에 현재 실행 중인 JVM 명령어의 주소를 저장하는 고유한 Program Counter Register
  • 현재 스레드가 어떤 명령어를 실행 중인지를 추적하고, 각 명령어가 실행될 때마다 다음 명령어의 주소로 업데이트

Native Method Stacks

JVM은 네이티브 메서드를 지원하기 위해 스택을 사용합니다. 네이티브 메서드는 자바 이외의 언어(예: C, C++)로 작성된 메서드를 나타냅니다

  • 스레드가 생성될 때 별도의 네이티브 메서드 스택이 할당됨
    • 네이티브 메서드 스택이 네이티브 메서드 호출(JNI: Java Native Interface) 및 관련 작업을 지원

Execution Engine

https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/

클래스 로더를 통해 JVM 내의 런타임 데이터 영역에 배치된 바이트코드는 실행 엔진에 의해 실행됩니다

실행 엔진은 자바 바이트코드를 명령어 단위로 읽어서 실행하며 CPU가 기계 명령어을 하나씩 실행하는 것과 비슷합니다 

  • 바이트코드의 각 명령어는 1바이트짜리 OpCode와 추가 피연산자로 이루어져 있으며, 실행 엔진은 하나의 OpCode를 가져와서 피연산자와 함께 작업을 수행한 다음, 다음 OpCode를 수행하는 식으로 동작한다.

자바 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경하는 방법이 Interpreter와 JIT Compiler가 있으며 GC가 힙 메모리에서 참조되지 않는 객체를 제거하여 메모리를 최적화하는 역할을 합니다

 

Interpreter

  • 바이트코드 명령어를 한 줄씩 읽고 실행
  • 한 줄씩 실행하기 때문에 인터프리터는 비교적 느림
  • 메서드가 여러 번호출될 때마다 매번 새로운 해석이 필요하다
  • 바이트코드 언어는  기본적으로 인터프리터 방식을 사용 

JIT(Just-In-Time) Compiler

  • 인터프리터의 단점을 보완하기 위해 도입
  • 인터프리터 방식으로 실행하다가 적절한 시점에 바이트코드 전체를 컴파일하여 네이티브 코드로 변경하고, 이후에는 해당 메서드를 더 이상 인터프리팅하지 않고 네이티브 코드로 직접 실행하는 방식

작동 방식

  • 인터프리터와 JIT 컴파일: 초기에 프로그램은 인터프리터를 사용하여 바이트 코드를 실행합니다. 인터프리터는 바이트 코드를 한 줄씩 해석하며 실행
  • 반복 코드 감지: 실행 중에 JIT 컴파일러는 반복적으로 호출되는 코드 블록을 감지합니다. 이러한 코드 블록을 "핫스팟"이라고 함
  • JIT 컴파일: JIT 컴파일러는 핫스팟 코드를 컴파일하여 네이티브 기계 코드로 변환합니다. 이렇게 컴파일된 코드는 이후에 반복되는 메서드 호출에 직접 사용

네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 계속 빠르게 수행

적절한 시점?

JVM의 구현에 따라 다를 수 있으며 일반적으로 다음 시나리오에서 JIT 컴파일이 발생

  • 메서드 호출 횟수: 메서드가 여러 번 호출되면, JVM은 해당 메서드를 JIT 컴파일하여 네이티브 코드로 변환하는 시점을 고려할 수 있습니다. 이는 같은 메서드가 반복해서 호출될 때 성능을 향상시키는 데 도움이 됩니다.
  • HotSpot Detection: HotSpot은 실행 중인 코드에서 가장 빈번하게 실행되는 부분을 식별하고, 해당 부분을 JIT 컴파일하여 최적화합니다. 이것은 프로그램의 실행 중에 동적으로 결정됩니다.
  • 초기 컴파일: 일부 JVM 구현은 클래스를 로드할 때 바로 JIT 컴파일을 수행하는 경우가 있습니다. 이것은 클래스를 처음 사용할 때 성능을 향상시키는 데 도움이 됩니다.
  • 메서드 인라인: JIT 컴파일러는 메서드 호출을 인라인으로 처리할 수 있으며, 이때 인라인으로 처리할지 여부는 컴파일 시점에 결정됩니다.

GC(Garbage Collector)

힙(Heap) 영역에서 더 이상 참조되지 않는 객체를 수집하고 제거하는 프로세스를 의미

가비지 컬렉션은 두 단계로 이루어지며 여기서는 자세히 다루지 않겠습니다

  • Mark(표시)
    • 가비지 컬렉터가 메모리 내에서 사용되지 않는 객체를 식별
  • Sweep(제거)
    • 가비지 컬렉터가 이전 단계에서 식별된 객체를 제거

 

https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/

 

이제 남은 요소 중 Native Method Interface(JNI)와 Native Method Library에 대해서 알아보겠습니다

 

Native Method Interface(JNI)

Java에서 네이티브(non-Java) 코드를 사용해야 할 때 필요한 기술로, 하드웨어와의 상호 작용이나 Java의 메모리 관리 및 성능 제약을 극복해야 하는 경우에 사용됩니다. JNI는 Java에서 네이티브 코드의 실행을 지원

  • JNI는 다른 프로그래밍 언어인 C, C++ 등을 지원하는 패키지를 허용하는 다리 역할
  • Java에서 완전히 지원되지 않는 코드를 작성해야 하는 경우 유용
  • native 키워드를 사용해서 자바 클래스내에서 C, C++의 패키지를 가져다가 쓸 수 있음

Native Method Libraries

C, C++, 어셈블리 등 다른 프로그래밍 언어로 작성된 라이브러리로, 일반적으로 .dll 또는 .so 파일 형태로 제공

  • Native Method Libraries는 JNI를 통해 로드될 수 있음
  • JNI를 사용하여 Java에서 네이티브 코드를 호출하고 연동할 수 있게 됨

예시

#include <stdio.h>
#include "HelloWorld.h"

JNIEXPORT void JNICALL Java_HelloWorld_printHello(JNIEnv *env, jobject obj) {
    printf("Hello, JNI!\n");
}
  • C로 작성된 네이티브 라이브러리를 작성
public class HelloWorld {
    public native void printHello();
    static {
        System.loadLibrary("HelloWorld");
    }
    public static void main(String[] args) {
        new HelloWorld().printHello();
    }
}
// sout: Hello, JNI!
  • `native`키워드를 사용하여 네이티브 메서드를 선언
  • `System.loadLibrary("HelloWorld")`을 통해 네이티브 라이브러리를 메모리에 로드
  • Java 클래스에서 네이티브 메서드 호출

References

 

https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/

 

JVM Tutorial - Java Virtual Machine Architecture Explained for Beginners

Whether you have used Java to develop programs or not, you might have heard about the Java Virtual Machine (JVM) at some point or another. JVM is the core of the Java ecosystem, and makes it possible for Java-based software programs to follow the "write on

www.freecodecamp.org

https://d2.naver.com/helloworld/1230

 

반응형

'Java' 카테고리의 다른 글

모던 자바 인 액션 - PART 6  (1) 2023.10.04
모던 자바 인 액션 PART - 4  (0) 2023.09.03
모던 자바 인 액션 - PART 3  (0) 2023.08.29
모던 자바 인 액션 - PART 2  (0) 2023.08.20
모던 자바 인 액션 - PART1  (0) 2023.08.19