[Java] JVM

N’oublie pas de t’aimer·2025년 1월 30일

Java

목록 보기
16/18

자바 코드 실행 과정

위 그림은 자바 코드의 실행 과정을 간략하게 보여 준다.

  1. 프로그램이 실행되면 JVM은 OS로부터 이 프로그램이 필요로 하는 메모리를 할당받는다. (JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.)
  2. 자바 파일(.java)이 자바 컴파일러에 의해 자바 바이트 코드(.class)로 변환된다.
  3. 클래스 로더를 통해 자바 바이트 코드를 JVM으로 필요한 시점에 로딩한다.
  4. 해석된 바이트 코드는 런타임 데이터 영역에 배치되어 실질적인 수행이 이루어지게 된다.
  5. 실행 과정 속에서 JVM은 필요에 따라 GC와 같은 관리 작업을 수행한다.

JVM의 구조

클래스 로더

자바는 동적 로드, 즉 컴파일 타임이 아니라 런타임(바이트 코드를 실행할 때)에 클래스를 로드하고 링크하는 특징이 있다. 이 동적 로드를 담당하는 부분이 JVM의 클래스 로더이다. 클래스 로더에는 로딩, 링크, 초기화 단계로 나뉘어져 있는데, 간단히 설명하면 다음과 같다.

로드

  • 각 자바 바이트 코드(.class)는 JVM에 의해 메소드 영역에 다음 정보를 저장한다.
    - 로드된 클래스를 비롯한 그의 부모 클래스의 정보
    - 클래스 파일과 Class, Interface, Enum의 관련 여부
    - 변수나 메소드 등의 정보
  • 링크
    - 검증: 읽어 들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사한다.
    - 준비: 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드, 메소드, 인터페이스를 나타내는 데이터 구조를 준비한다.
    - 분석: 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체한다.
  • 초기화
    - 클래스 변수를 적절한 값으로 초기화한다. 즉, static 필드들이 설정된 값으로 초기화된다.

런타임 데이터 영역

런타임 데이터 영역은 JVM이 운영 체제 위에서 실행될 때, 할당 받는 메모리 영역이며 총 6개의 영역으로 나눌 수 있다. 이 영역들은 스레드가 공유하는 공간인지 아닌지로 나눈다.

  • 스레드마다 하나씩 생성되는 공간
    - PC 레지스터
    • JVM 스택
    • 네이티브 메소드 스택
  • 모든 스레드가 공유하는 공간
    - 힙
    • 메소드
    • 런타임 상수 풀

PC 레지스터

  • PC 레지스터는 각 스레드마다 하나씩 존재하며, 스레드가 시작될 때 생성된다.
  • PC 레지스터는 메소드 안에서 바이트 코드 몇 번째 줄을 실행하고 있는지와 같은 정보를 갖고 있다.

JVM 스택

  • JVM 스택은 각 스레드마다 하나씩 존재하며, 스레드가 시작될 때 생성된다.
  • JVM 스택은 스택 프레임이라는 구조체로 이루어져 있는데, 새로운 메소드가 호출될 때마다 push, 메소드 실행이 끝나면 pop 동작을 수행한다.
  • 각 스택 프레임은 지역 변수 배열, 피연산자, 프레임 데이터를 갖는다.
    - 프레임 데이터는 현재 실행 중인 메소드가 속한 클래스의 런타임 상수 풀, 이전 스택 프레임에 대한 정보, 현재 메소드가 속한 클래스, 객체에 대한 참조 등을 말한다.

네이티브 메소드 스택

  • 네이티브 메소드 스택은 자바 바이트 코드가 아닌 다른 언어로 작성된 네이티브 코드를 위한 스택이다.
  • 성능 향상을 목적으로 작성되었다.
  • JVM 스택과 네이티브 스택이 나뉘어져 있다 하더라도 자바 코드를 수행하다 JNI를 호출하면, JVM 스택에서 네이티브 메소드 스택으로 확장할 뿐이다.

  • 힙은 모든 스레드가 공유하는 영역이다.
  • 힙은 프로그램을 실행하면서 생성된 모든 인스턴스 또는 객체를 저장하는 공간이다.

메소드 영역

  • 모든 스레드가 공유하는 영역으로 JVM이 시작될 때 생성된다.
  • 클래스 로더가 클래스 파일을 읽어 오면, 클래스 정보를 파싱하여 런타임 상수 풀, 필드와 메소드 정보, static 변수, 메소드의 바이트 코드 등을 보관한다.
  • 메소드 영역은 JVM 벤더마다 다양한 형태로 구현할 수 있으며, 오라클 핫스팟 JVM에서는 흔히 PermGen(자바 1.7 이전), MetaSpace(자바 1.8 이후)로 부른다.
  • 메소드 영역에 대한 GC도 JVM 벤더의 선택 사항이다.

런타임 상수 풀

  • 런타임 상수 풀은 메소드 영역에 포함되는 영역이긴 하지만, JVM 동작에서 가장 핵심적인 역할을 수행하는 곳이기 때문에 JVM 명세에서도 따로 중요하게 기술한다.
  • 각 클래스와 인터페이스의 상수 뿐만 아니라, 메소드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블이다.
  • 즉, 어떤 메소드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메소드나 필드의 실제 메모리 상 주소를 찾아서 참조한다.

실행 엔진

  • 실행 엔진은 메모리에 적재된 바이트 코드(.class)를 기계어로 변경하여 명령어(instruction) 단위로 실행 및 바이트 코드를 운영체제에 맞게 해석해주는 역할을 수행한다. 실행 엔진이 바이트 코드를 명령어 단위로 읽어서 수행하는데 크게 두 가지 방식이 사용된다.
    - 인터프리터
    - 런타임 중에 바이트 코드를 한 줄씩 읽고 실행한다.
    - 컴파일보다 속도가 느리다.
    - JIT (Just In Time)
    - 인터프리터의 속도 이슈를 해결하기 위해 같이 사용한다.
    - 자주 실행되는 바이트 코드 영역을 런타임 중에 기계어로 컴파일하여 사용한다.
profile
매일 1퍼센트씩 나아지기 ୧(﹒︠ ̫ ̫̊ ̫﹒︡)୨

0개의 댓글