[Java] JVM이란? - JVM 구조 및 실행 과정

Erin Lee·2023년 12월 1일
0

JVM

1. JVM이란?


JVM은 Java가 실행되고 작업을 수행하게 해주는 가상 엔진이다.
JRE(Java Runtime Environment)의 일부며 Java가 어느 운영체제 상에서도 작동할 수 있게 해주는 역할을 해준다.

factorio thumbnail

.java 파일은 javac 컴파일러를 통해 바이트 코드로 작성된 .class 파일로 컴파일되고 .class 파일은 JVM에 로드되어 각 실행되기 때문에 어느 OS에서든지 실행이 가능하다.


2. JVM의 구조


JVM의 구조는 크게 JVM에 어플리케이션이 정상적으로 로드될 수 있게 해주는 Class Loader, 로드된 파일들이 수행되는 영역인 Runtime Data Area, Native 메서드를 다루는 Native Method Interface, 로드된 .class 파일을 바이트 코드로 실행시켜주는 Execution Engine으로 이루어져 있다.

factorio thumbnail


3. JVM에서의 실행 과정


  1. 클래스 로더(Class Loader)를 통해 Java 프로그램을 실행하기 위한 모든 .class 파일들을 올린다.
    Java Compiler를 통해 .java 파일에서 .class 파일로 변환된다. 변환된 .class 파일들을 모아 JVM에 동적으로 로딩한다.
    로딩 과정은 다음과 같은 단계로 수행된다.

    Class Loader

    1-1. Loading : .class 파일을 JVM에 올리는 과정이다.

    Loading 과정에서 파일을 업로드할 때 각각의 .class 파일들이 기본으로 제공받은 파일인지 개발자가 정의한 파일인지 등에 따라 맞는 클래스 로더에 따라서 로딩된다.
    이때의 클래스 로더의 수준은 Bootstrap Class Loader, Extension Class Loader, Application Class Loader가 있다.

    • Bootstrap Class Loader
      JVM을 실행시키기 위한 필수 라이브러리(rt.jar 등)을 로드한다.
      가장 상위 클래스 로더로 운영체제에 맞게 네이티브 코드로 구현되어있다.
      *JDK9부터는 rt.jar 파일이 없다. JDK8까지는 rt.jar 파일에 필요한 모든 파일을 넣었지만 JDK9부터는 jar 기반 방식이 개선되어 완전히 모듈화가 되면서 필요한 파일만 실행될 수 있도록 바뀌었다.
      그래서 rt.jar 파일을 작은 모듈로 나눠졌다.
    • Extension Class Loader
      Bootstrap Class Loader를 부모로 갖는 클래스 로더로 확장 자바 클래스들을 로드한다.
      확장 자바 클래스란 ${JAVA_HOME}/lib/ext 경로에 있는 클래스들을 말한다.
    • Application Class Loader
      Application Class Loader는 어플리케이션 레벨의 클래스들을 로드한다.
      어플리케이션 레벨의 클래스란 class path에 있는 클래스 파일들을 말하며 개발자가 작성한 클래스이다.
      각 클래스 로더에서 클래스를 찾고 없는 경우 ClassNotFoundException을 발생시킨다.

    1-2. Linking : 로드된 .class 파일들을 사용하기 위해 검증하고 사용할 준비하는 과정이다.
    클래스나 인터페이스를 연결하기 위해선 해당 클래스나 인터페이스들을 검증하고 준비해야한다.

    • Verification(검증)
      JVM에서 사용이 가능한 형태인지를 검증하는 단계
      .class 파일 형식이 유효한지 검사한다.
    • Preparation(준비)
      Type이 필요로 하는 Memory를 할당해 주는 단계
      클래스가 필요로 하는 메모리를 할당해주고 클래스의 필드, 메서드, 인터페이스를 나타내는 데이터 구조를 준비한다.
      메모리가 할당되는 것까지만 이뤄지고 초기화 되어 원래의 값이 할당되는 것은 다음 단계 Initialization에서 이뤄진다.
    • Resolution(분석)
      JVM의 할당된 메모리 참조를 Method Area에 참조된 메모리 주소값으로 바꾸는 단계

    1-3. Initialization : 검증이 완료되면 클래스 변수에 메모리를 할당하고 변수 유형에 따라 원래의 값으로 초기화되어 실행되는 단계이다.
    .class 파일의 코드를 읽어 Java 코드에서의 class와 interface의 값들을 지정한 값들로 초기화하고 메서드를 실행시켜준다.
    이 과정은 멀티 쓰레딩으로 이뤄져 동시에 여러 class가 초기화를 진행한다.
    이제 JVM에 .class 파일이 로드되어 구동시킬 준비는 끝이 났다.

  2. JVM에 전달된 클래스 정보들이 Runtime Data Area에 로딩된다.

    Runtime Data Area

    Runtime Data Area는 JVM에 로드되고 초기화된 클래스 파일들이 저장되는 영역, 실행될 때 필요한 메모리들을 운영체제로부터 할당받아 저장하는 영역이다.

    Runtime Data Area 구조
    Runtime Data Area는 크게 Method Area, Heap, Java Stacks, PC Registers, Native Stacks로 구성되어있다.

factorio thumbnail

  • Method Area
    Method Area 영역은 Runtime Constant Pool(런타임 상수 풀), 클래스의 필드, 생성자가 저장되는 영역이다.
    Method Area는 JVM 시작 시 생성되며 JVM 종료 시 소멸되고 JVM이 작업 시에 모든 Thread 간 자원이 공유되는 영역이다. → JVM당 하나의 Method Area가 존재
    Method Area는 JDK 7까지는 Permanent Generation(PermGen)이라고 불렀고 JDK8부터는 Metaspace로 대체되었다.
    PermGen은 메모리 크기가 고정되어 있기 때문에 Garbage Collection의 작업이 자주 수행되어야 했고 그만큼 비용이 들었다. 제한된 메모리로 생기는 에러가 OutOfMemoryError이다.
    메모리의 크기를 수동으로 늘려야했고 메모리 부족으로 인한 불편함으로 인해 JDK8부터는 제거가 되었다.
    대시 Metaspace 영역이 도입되었고 기존 Method Area의 단점을 보완하여 자유롭게 공간의 크기를 조절할 수 있게 되었다.
    Metaspace는 수동적으로 영역을 지정해주는 것이 아닌 OS가 동적으로 필요한 메모리만큼 제공해주기 때문이다.
    또한 최대 메모리 영역에 도달하면 자동으로 GC가 더이상 사용하지 않는 클래스 데이터들을 제거해줘 효율적으로 관리가 가능해졌다.

Method AreaMetaspace
JDK8 이후부터 삭제JDK8부터 도입
항상 최대 크기가 고정기본 OS에 따라 자동으로 크기를 늘림
연속적인 Java Heap 메모리기본 OS에서 제공하는 메모리
비효율적인 Garbage Collection효율적인 Garbage Collection

  • Heap
    모든 객체들(클래스, 인스턴스들)과 해당 인스턴스 변수들이 저장되는 영역이다.
    자동으로 GC를 통해서 저장공간이 관리되며 만약 메모리가 가득 차게 되면 OutOfMemoryError를 발생시킨다.
    Method Area와 동일하게 JVM당 하나만 생성되고 Thread 간 공유된다.
    Heap의 메모리는 크게 Yong Generation, Old Generation으로 나뉜다.
    • Young Generation : 새로 생성된 객체가 저장되는 영역이다. 해당 영역이 채워지면 자동으로 GC 작업이 이뤄지는데 이 작업을 Minor GC라고 한다.
    • Old Generation : Young Generation에서 GC 이후에 남은 객체가 저장되는 영역이다. 이 영역 또한 메모리가 채워질 때마다 GC가 이뤄지며 Major GC라고 한다.

  • Java Stacks
    각 Thread별로 할당되는 영역으로 지역 변수, 매서드의 매개 변수, 임시적으로 사용되는 변수, 메서드의 정보 등이 저장되는 영역이다.
    메서드가 호출될 때마다 메모리 공간인 스택 프레임(Stack Frame)이 생성되며 메서드가 작동되는데 필요한 메모리 공간을 제공해준다. 그리고 메서드가 수행되는 동안 필요한 지역 변수들과 결과값들이 저장되고 메서드가 종료되면 해당 스택 프레임은 사라진다.
    Java Stacks 영역이 가득차게 되면 StackOverFlowError를 발생시킨다.

  • Native Stacks
    Java로 작성된 프로그램을 실행할 때 Java로만 구성된 코드로만 이뤄지지 않는다. C, C++과 같은 순수 코드로 작성된 메서드(Native Method)들이 있는데 이러한 Native Method들을 다루는 영역이다.

  • PC Registers(Program Counter Registers)
    PC Registers는 JVM에서 수행되고 있는 스레드의 명령어 주소값이 저장되는 영역이다.
    어떤 시점에서든지 JVM은 멀티 쓰레드 방식으로 수행하기 때문에 동시에 여러 쓰레들 지원한다. 그렇기 때문에 쓰레드별로 동시에 실행되고 있는 것에 대한 정보를 저장하는 것이 필요하다.
    만약 현재 수행되고 있는 메서드가 Native 메서드가 아닌 경우에는 PC Registers에는 현재 실행중인 메서드의 명령어 주소가 저장된다. Native 메서드라면 PC Registers에 저장되지 않는다.

  1. JVM의 Runtime Data Area에 로딩된 .class 파일이 Excecution Engine(실행 엔진)을 통해 실행된다.
    Execution Engine을 통해 Runtime Data Area에 있는 바이트 코드를 기계어로 변환해서 실행한다.
    변환 방식으로는 InterpreterJIT(Just In Time) Compiler를 사용하는 방식으로 두가지가 있다.

    • Interperter(인터프리터)
      바이트 코드를 한줄한줄 해석하고 실행하는 역할을 한다.
      그래서 명령어를 처음에 시작하는 속도는 컴파일러보다 빠르지만 전체적인 수행 속도는 느리다.
      또한 한줄한줄 수행되다보니 동일한 바이트 코드를 번역하더라도 새로 해석해서 수행해야한다.
    • JIT(Just In Time)
      인터프리터의 속도 문제를 해결하기 위해 디자인된 기능이다.
      인터프리터와 다르게 한줄한줄 읽는 것이 아닌 바이트 코드를 한번에 읽어 번역한다.
      그래서 처음에 시작하는 속도는 느릴 수 있지만 전체적인 수행 속도는 빠르다.

    JVM은 이 두가지 방식을 결합하여 사용한다.

    프로그램이 처음 실행될 때에는 인터프리터를 통해 빠르게 실행되지만 일정한 코드 블록이나 메소드가 자주 실행되는 경우에는 JIT 컴파일러를 통해 최적화시킨다.

  2. 기계어로 해석된 바이트 코드는 Runtime Data Areas에 배치되어 스레드 동기화나 이 과정 안에서 GC가 이뤄지는 등 실질적인 수행이 이루어진다.


정리하며


Java가 OS에 독립적인 이유는 JVM 때문이다.
JVM의 구조, Java 프로젝트가 어떻게 수행되는지, 클래스 파일들이 메모리에 어떻게 저장되는지 등에 대해 이해하기 위해 JVM에 대해 정리하였다.
다음은 GC에 대해서 정리해볼 예정이다. GC도 버전별 차이가 있고 버전별 차이가 있는 이유 등에 대해서 공부하면 많은 도움이 될 것 같다.


출처
https://tecoble.techcourse.co.kr/post/2021-07-15-jvm-classloader/
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3.1
https://docs.oracle.com/javase/tutorial/ext/basics/load.html
https://programmerbay.com/introduction-to-jvm-and-its-architecture/
https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/
https://medium.com/@gsy4568/jvm의-동작은-execution-engine-ed2480176a15
https://d2.naver.com/helloworld/1230

profile
내가 설명할 수 있어야 비로소 내가 아는 것이다

0개의 댓글