본문 바로가기

Java

JVM 구조와 Java의 실행방식

  • JVM(Java Virtual Machine) : 자바 바이트 코드를 해석하고 실행하는 것을 말한다.

JVM 특징

  • 스택 기반의 가상 머신
  • 심볼릭 레퍼런스
    • 기본 자료형(primitive data type)을 제외한 모든 타입(클래스와 인터페이스)을 명시적인 메모리 주소 기반의 레퍼런스가 아니라 심볼릭 레퍼런스를 통해 참조한다.
    • 참고하는 클래스의 특정 메모리 주소를 참조 관계로 구성한 것이 아닌 참조하는 대상의 이름만을 지칭한 것이다.
  • 가비지 컬렉션(garbage collection)
    • 클래스 인스턴스는 사용자 코드에 의해 명시적으로 생성되고 가비지 컬렉션에 의해 자동으로 파괴된다.
  • 기본 자료형을 명확하게 정의하여 플랫폼 독립성을 보장
    • C/C++ 등의 전통적인 언어는 플랫폼에 따라 int형의 크기가 변한다.
    • JVM은 기본 자료형을 명확하게 정의하여 호환성을 유지하고 플랫폼 독립성을 보장한다.
  • 네트워크 바이트 오더(network byte order)
    • 자바 클래스 파일은 네트워크 바이트 오더를 사용한다.
    • 리틀 엔다안이나, 빅 엔디안 사이에서 플랫폼 독립성을 유지하려면 고정된 바이트 오더를 유지해야 하므로 네트워크 전송 시에 사용하는 바이트 오더인 네트워크 바이트 오더를 사용한다.
    • 네트워크 바이트 오더는 빅 엔디안이다.
      • 빅 엔디안은 최상위 비트부터 저장하는 방식이며 리틀 엔디안은 최하위 비트부터 저장하는 방식이다.

 

JVM 구조

JVM 구조 - Navar D2

  1. Class Loader : 런타임에 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크한다.
    • 계층 구조 : 클래스 로더끼리 부모-자식 관계를 이라어 계층 구조로 생성된다.
      최상위 클래스 로더는 부트스트랩 클래스 로더(Bootstrap Class Loader)이다.
    • 위임 모델 : 클래스를 로드할 때 먼저 상위 클래스 로더를 파악하여 상위 클래스 로더에 있다면 해당 클래스를 사용하고, 없다면 로드를 요청받은 클래스 로더가 클래스를 로더한다.
      이전에 로드된 클래스인지 클래스 로더 캐시를 확인하고, 없으면 상위 클래스 로더를 거슬러 올라가며 확인한다.
      부틋스트랩 클래스 로더까지 확인해도 없으면 요청받은 클래스 로더가 파일 시스템에서 해당 클래스를 찾는다.
    • 가시성(visibility) 제한 : 하위 클래스 로더는 상위 클래스 로더의 클래스를 찾을 수 있지만, 상위 클래스 로더는 하위 클래스 로더의 클래스를 찾을 수 없다.
    • 언로드 불가
    • 클래스 로드 단계 : 로드 → 검증 → 준비 → 분석 → 초기화
  2. Excution Engine : Runtime Data Areas에 할당된 바이트코드를 실행시키는 일을 수행한다.
    • 바이트 코드를 명령어 단위로 읽고 번역하는 방법
      • Interpreter 방식 : 각 OS 플랫폼에 맞는 인터프리터가 바이트 코드를 실행하는데, 바이트 코드를 한 라인씩 읽고 실행을 하게 된다.
        하지만, 한 라인씩 읽는 방식으로 인해 속도 문제가 발생한다.
      • JIT(Just In Time) 컴파일 혹은 동적 번역 방식 : 자주 사용되는 바이트 코드 영역을 매번 해석하지 않고 캐싱을 하여 재호출이 일어나면 캐싱된 코드를 사용하므로 속도가 조금 빨라지게 되는 것이다.
  3. Garbage Collection : JVM의 성능과 가장 연관된 Runtime Data Areas의 Heap 영역의 동적 할당 데이터들을 관리하는 역할을 한다.
  1. Runtime Data Areas : JVM이라는 프로그램 운영체제 위에서 실행되면서 할당받는 메모리 영역이다.

Runtime Data Areas - Naver D2

  • PC Register : 현재 수행중인 JVM 명령의 주소를 갖는다.
  • JVM Stack : 스텍 프레임(Stack Frame)이라는 구조체를 저장하는 스택으로 JVM은 오직 JVM 스택에 스택 프레임을 push pop하는 동작만 수행한다.
    • 스택 프레임 : JVM 내에서 메서드가 수행될 때마다 push pop 된다.
      지역 변수 배열, 피연산자 스택, 현재 실행 중인 메서드가 속한 클래스의 런타임 상수 풀에 대한 레퍼런스를 갖는다.
      스택 프레임의 크기도 메서드에 따라 크기가 고정된다.
    • 지역 변수 배열 : 0부터 시작하는 인덱스를 가진 배열로 0은 this 레퍼런스이고, 1부터 메서드에 전달된 파라미터들이 저장되며, 메서드 파라미터 이후에는 지역 변수들이 저장된다.
    • 피연한자 스택 : 메서드의 실제 작업 공간이며 피연산자 스택의 크기도 컴파일 시에 결정된다.
  • Native Method Stack : 자바 외의 언어로 작성된 네이트브 코드를 위한 스택이다.
    JNI(Java Native Interface)를 통해 호출하는 C/C++ 등의 코드를 수행하기 위한 스택으로, 언어에 맞게 C 스택이나 C++ 스택이 생성된다.
  • Heap : 인스턴스 또는 객체를 저장하는 공간으로 가비지 컬렉션 대상이다.
    JVM 성능 등의 이슈에서 가장 많이 언급되는 공간이다.
    힙 구성 방식이나 가비지 컬렉션 방법 등은 JVM 벤더의 재량이다.
  • Method Area : JVM이 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메서드 정보, Static 변수, 메서드의 바이트 코드 등을 보관한다.
    메서드 영역에 대한 가비지 컬렉션은 JVM 벤더의 선택 사항이다.
  • Runtime Constant Pool : 각 클래스와 인터페이스의 상수뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블이다.
    어떤 메서드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조한다.

 

 

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