[자바, Java] JVM - 자바 가상머신(Java Virtual Machine)의 구조

JVM 학습



자바의 리플렉션 개념에 대해 공부하면서 JVM도 자연스럽게 같이 공부하게 되었다.

자바의 성능 개선을 위해서라도 JVM에 대한 공부는 중요하다고 생각한다.

휘발성이 높은 개념이라 생각되어 아주 간단하게라도 글로 정리해보고자 한다.


1. JVM, JRE, JDK

먼저 이 3가지의 관계 정리를 통해, JVM의 역할에 대한 이해를 더 돕고자 한다.


1) JVM

  • ‘자바 가상 머신(Java Virtual Machine)’이다.

  • 자바 바이트 코드(.class 파일)를, 인터프리터와 JIT 컴파일러를 이용하여 OS에 특화된 코드로 변환하여 실행한다.
    • 이때, OS로부터 메모리를 할당해주는 역할도 한다.
    • 각각의 플랫폼(윈도우, 리눅스 등)마다의 JVM이 다르다.
    • 이 특징은 JAVA가 OS에 구애받지 않고 재사용을 가능하게 해주는 장점으로 연결된다.
  • 특정 플랫폼에 종속적이다.
    • 또한 각각의 플랫폼(윈도우, 리눅스 등)마다의 JVM이 다르기 때문에 플랫폼에 종속적이기도 하다.
  • 메모리 관리를 해준다.
    • 그 유명한 Garbage Collection(GC, 가비지 컬렉션)이 JVM에 속한다.
    • GC : 더이상 참조되지 않는 객체를 모아서 정리한다.


2) JRE

  • ‘자바 실행환경’(Java Runtime Environment)이다.
  • JVM + 라이브러리
    • JVM이 자바 프로그램을 동작시킬 때 필요한 ‘핵심 라이브러리 및 자바 런타임 환경에서 사용하는 프로퍼티 세팅이나 리소스 파일’을 가지고 있다.
    • 컴파일 된 자바 프로그램을 실행하는 데에 필요한 패키지이다.

githubGraph


3) JDK

  • ‘자바 개발도구(Java Development Kit)’이다.
  • JRE + 개발에 필요할 툴
    • 사실 개발자들은 개발을 해야하기 때문에 JRE만 다운받는 일은 거의 없다.
    • 그래서 오라클은 자바 11부터는 JDK만 제공하며 JRE를 따로 제공하지 않는다.

githubGraph


2. JVM의 구조

  • JVM의 5가지 영역
    1. 클래스 로더 시스템
      • .class 에서 바이트코드를 읽고 메모리에 저장한다.
    2. 메모리
      • 클래스 로더를 통해 로드된 클래스들과 실행 과정의 정보들을 저장한다.
    3. 실행 엔진
      • ‘인터프리터와 JIT 컴파일러’를 통해 바이트 코드를 읽고 실행한다.
      • ‘Garbage Collection(GC, 가비지 컬렉션)’을 통해 메모리를 정리한다.
    4. 네이티브 메소드 인터페이스(JNI)
      • 자바 애플리케이션에서 C, C++, 어셈블리로 작성된 함수를 사용할 수 있는 방법을 제공한다.
        • Native 키워드를 사용하여 네이티브 메소드를 호출할 수 있다.
    5. 네이티브 메소드 라이브러리
      • C, C++로 작성 된 라이브러리이다.
  • 이 중, ‘메모리 영역’에 대해 조금 더 구체적으로 들어가보자.


1) JVM 메모리의 구조

  • JVM 메모리의 5가지 영역
    1. 스택(stack)
      • 각 쓰레드마다 하나의 런타임 스택을 만든다.
      • 메소드 호출을 ‘스택 프레임’이라 부르는 블럭으로 쌓고, 쓰레드를 종료하면 런타임 스택도 사라진다.
        • 스택 프레임마다 각 메소드의 지역변수, 인자값, 리턴값이 저장된다.
    2. PC(Program Counter) 레지스터
      • 각 쓰레드마다 가장 최근에 실행되고 있는 메소드를 가리키는 역할을 맡는다.
    3. 네이티브 메소드 스택
      • 네이티브 방식의 메소드란 Java가 아닌 다른 언어로 작성된 코드를 의미한다.
      • 일반적으로 JVM은 네이티브 방식을 지원한다.
        • 쓰레드에서 네이티브 방식의 메소드가 실행되는 경우, Native Method Stack에 쌓이게 된다.
      • 일반적인 메소드를 실행하는 경우 동작 과정
        1. JVM 스택에 쌓인다.
        2. 해당 메소드 내부에 네이티브 방식을 사용하는 메소드 ( ex) c언어로 작성된 메소드)가 있으면, 해당 메소드는 네이티브 스택에 쌓인다.
    4. 힙(heap)
      • 모든 쓰레드가 공유하는 영역이다.
      • 자바 프로그램에서 사용되는 모든 인스턴스 변수(객체)들이 저장된다.
        • 자바에서는 new를 사용하여 객체를 생성하면 힙 영역에 저장된다.
    5. 메소드(method)
      • 모든 쓰레드가 공유하는 영역이다.
      • 클래스 수준의 정보들 (클래스 이름, 부모 클래스 이름, 생성자, 메소드, 변수 등) 을 저장한다.
        • static, 상수 등 구분없이 모든 클래스들의 정보가 저장된다.


2) 리플렉션을 배우기 전에 명확히 집고 넘어가야 할 부분

  1. 클래스의 완전한 이름은 ‘패키지 이름, 클래스 이름, 클래스 로더 이름’으로 구성되어 있다.
    • 즉, 패키지 이름과 클래스 이름까지 같다고 해도, 각 클래스를 로딩한 클래스 로더가 ‘다른 클래스 로더’라면 충돌이 일어나지 않는다.
    • 반대로 같은 객체가 아니기 때문에, 같은 객체로 취급하고 작업을 진행하면 원하지 않는 방향으로 동작된다.
      • 다음 글인 리플렉션 정리 글에서 ByteBuddy의 예제를 보면 알 수 있다.
  2. 코드상에서 ‘new 연산자’ 혹은 ‘클래스.class’ 등등을 통해 클래스가 한 번이라도 로드되었을 때, 힙 영역‘Class<T>‘ 형식의 객체로 저장이 된다.
    • 모든 리플렉션의 시작은 ‘Class<T>‘ 부터 시작한다.
    • 참고로 클래스 로딩 후 만들어지는 ‘Class<Book>’ 객체new 연산자를 통해 생성된 ‘Book’ 객체는 다른 객체임을 인지하자.
  3. ‘인스턴스 변수.getClass()’ 혹은 ‘클래스 이름.class’ 를 통해 힙 영역에 저장되어있는 ‘Class<T>‘ 에 접근할 수 있다.




© 2021. All rights reserved.

----------Powered by Hydejack----------

Jun's Development Blog