JVM

Nahyeon.In·2024년 5월 19일
0

What is the JVM?

JVM(Java Virtual Machine)은 JAVA를 실행하기 위한 가상머신이다.
Java application은 WORA (Write Once Run Anywhere)이라고도 불린다. 이는 한 OS에서 Java 코드가 작성된다면, Java 지원 시스템에서도 조정 없이 실행될 수 있다는 것을 의미한다. 그리고 JVM이 이러한 역할을 수행한다.

즉, JVM은 한 CPU가 한 OS에 의존하지 않고 Java를 인식하고 실행할 수 있도록 하는 가상머신이다.

".java" file은 CPU에 의해 인식될 수 없기 때문에 기계어(machine code)로 compile 되어야 한다.
하지만 Java는 이 JVM 이라는 가상머신을 거쳐서 OS에 도달하기 때문에 OS가 인식할 수 있는 기계어로 바로 컴파일 되는게 아니라 JVM이 인식할 수 있는 Java bytecode(".class" file)로 변환된다.

이렇게 변환된 bytecode 역시 기계어가 아니기 때문에 OS에서 바로 실행할 수 없다. JVM은 OS가 bytecode를 이해할 수 있도록 해석해준다. 이를 통해 bytecode가 특정 OS에 의존하지 않고 JVM 위에서 실행될 수 있게 된다.


JVM Architecture

JVM은 Class Loader, JVM Memory, Execution Engine 등을 포함하고 있다.

Class Loader

Class Loader은 주로 아래의 세가지 작업을 담당한다.

  1. Loading
  2. Linking
  3. Initialization

풀어서 설명하자면, Class Loader은 ".class" file을 JVM에 로딩하고, 이를 링크하여 JVM의 메모리 영역에 배치하는 역할을 담당한다.

Loading: Class loaderd은 “.class” file을 읽고, 그에 상응하는 binary data를 생성하고 method area에 저장한다.
Linking: 로드된 “.class” file을 사용하기 위해 검증하고 준비하는 과정이다.

  • Verification: “.class” file이 올바른지 확인한다. 파일이 올바르게 format되었는지, 유효한 compiler에 의해 생성이 되었는지 등을 검증한다. 검증에 실패했을 경우 java.lang.VerifyError라는 런타임 예외 발생하는데, 이는 ByteCodeVerifier라는 component에 의해 실행된다.
  • Preparation: JVM은 class 정적 변수(class static variables)에 대한 메모리를 할당하고 해당 메모리를 기본값으로 초기화 한다.
  • Resolution: 이 과정에서, symbolic reference가 direct reference로 대체된다. 즉, 실제 객체의 주소를 참조한다.

    Symnolic reference
    자바에서는 특정 객체를 참조할 때 Memory Address를 직접 참조하는게 아니라, 객체의 이름으로 참조한다. 이렇게 객체의 이름으로 참조하는 것은 Symbolic Reference라고 한다.

Initialization: 링크 과정 후, class file의 code를 읽고 모든 정적 변수에 정의된 값을 할당한다.

Runtime Data Area

프로그램을 수행하기 위해 OS에서 할당받은 메모리 공간으로 컴파일된 bytecode("class" file)이 메모리에 실행가능한 상태로 로드된 공간이다.

1. Method area: 메서드 영역에는 클래스 이름, 상위 클래스 이름, 메서드 및 변수 정보 등 모든 class level 정보가 저장되며, 여기에는 정적 변수도 포함된다. JVM당 메서드 영역은 하나만 존재하며, 이는 공유 자원(shared resource)이다.
2. Heap area: "new" 키워드를 사용하여 생성된 모든 객체의 정보가 힙 영역에 저장된다. 힙영역 역시 JVM당 하나만 존재하고 공유자원이다. 객체를 참조하는 변수나 field가 없다면 garbage collection에 의해 제거된다.
3. Stack area: stack area는 공유자원이 아니다. 각 스레드마다 JVM은 하나의 run-time stack을 생성하고 스택을 이곳에 저장한다. 이 스택의 각 블록은 activation record/stack fram라고 불리고 method의 호출을 저장한다. 해당 메서드의 모든 지역변수는 해당 stack fram에 저장된다. 스레드가 종료되면 해당 스레드의 run-time stack은 JVM에 의해 파괴된다.

4. PC Registers: 스레드의 현재 실행 중인 명령어의 주소를 저장한다. 각 스레드는 별도의 PC 레지스터를 가진다.
5. Native method stacks: 각 스레드마다 별도의 네이티브 스택이 생성된다. 이 스택은 네이티브 메서드 정보를 저장한다.

Execution Engine 

Execution engine은 "*.class"(bytecode)를 실행한다. Class Loader는 bytecode를 JVM의 Runtime Data Area영역에 배치하고, Execution engine이 이를 실행한다. bytecode를 한 줄씩 읽고, 다양한 메모리 영역에 있는 데이터와 정보를 사용하여 명령을 실행한다.

  • Interpreter: Bytecode를 한줄씩 해석하고 실행한다. 한 method가 여러번 호출될 때마다 매번 해석이 필요하기 때문에, 같은 method가 여러번 호출됐을 때 비효율적이다.
  • JIT(Just-In-Time Compiler): Interpreter의 효율성을 높이기 위해 사용된다. 전체 bytecode를 native code(운영 체제가 직접 실행할 수 있는 기계어)로 컴파일하고, JIT는는 직접적으로 native code를 제공한다. 이로인해 같은 method가 여러번 호출되더라도 반복적으로 해석할 필요가 없어진다. 하지만 한번 컴파일할때 비교적 시간이 오래 걸리기 때문에 한번만 호출하는 method일 경우 비효율적이게 된다.

English version
https://sapphire-beach-570.notion.site/JVM-0ec5cccdb71d4a09a820cf7984c52f14


📁 Reference
https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8
https://imbf.github.io/interview/2021/03/02/NAVER-Practical-Interview-Preparation-4.html

0개의 댓글