Blog
스터디
CS Study with SON
1주차
JVM

출처 - https://github.com/jmxx219/CS-Study (opens in a new tab)

Java Virtual Machine(JVM)


개요(JDK, JRE, JVM)

  • JDK(Java Development Kit)
    • 자바 개발 도구로, JRE + 개발에 필요한 툴
    • 자바 개발 환경으로 자바 어플리케이션을 개발하기 위해 필요한 도구를 제공함
    • 자바 언어를 바이트 코드로 컴파일 해주는 자바 컴파일러(javac), 자바 클래스 파일을 해석해주는 역 어셈블리어(javap) 등 존재
    • Tools and Commands Reference (opens in a new tab)
  • JRE(Java Runtime Environment)
    • 자바 실행 환경으로, 자바 애플리케이션을 실행할 수 있도록 구성된 배포판
    • JVM과 핵심 라이브러리 및 자바 어플리케이션 실행에 필요한 파일들을 포함함
  • JVM(Java Virtual Machine)
    • 자바 어플리케이션을 실행하는 가상 머신

VM

가상머신: 프로그램을 실행하기 위해 물리적 머신과 유사한 머신을 소프트웨어로 구현한 것

장점

  • 이식성
    • 플랫폼과 운영체제에 독립적으로 동작할 수 있도록 함
    • 개발한 애플리케이션은 VM 위에서 실행되므로, 어떤 운영체제나 플랫폼에서도 동일하게 작동할 수 있음
    • 이식성이 높아져서 애플리케이션을 다양한 환경에서 실행하거나 배포하는 데 유리함
  • 보안
    • 보안을 강화할 수 있는 장치를 제공
    • VM은 코드 실행과 메모리 관리를 감독하며, 액세스 제어와 코드 검증 등의 보안 기능을 제공함
    • 애플리케이션을 격리하여 악성 코드의 실행을 방지하고, 안전한 실행 환경을 제공함
  • 자원 관리
    • 메모리 할당, 가비지 컬렉션, 스레드 관리 등을 자동으로 처리하여 개발자가 직접 관리해야 하는 부담을 줄여줌
    • 이를 통해 안정성과 성능을 향상시칼 수 있음
  • 다중 언어 지원
    • JVM은 Java뿐만 아니라 Kotlin, Scala, Groovy 등 다른 JVM 언어들을 실행할 수 있음
    • 이는 개발자에게 언어 선택의 자유를 주고, 기존 언어와 새로운 언어 간의 상호 운용성을 제공함

단점

  • 성능 오버헤드
    • VM은 어플리케이션 실행을 위해 추가적인 추상화 계층을 도입하므로 성능에 일정한 오버헤드가 발생함
    • 일부 언어나 애플리케이션에서는 이 오버헤드가 큰 문제가 될 수 있음
    • 최근 VM은 최적화 기술을 통해 이러한 성능 문제를 완화하고 있음
  • 자원 사용
    • VM은 실행을 위해 일정한 시스템 자원이 필요함
    • 애플리케이션을 실행할 때 이에 대한 고려가 필요하며, 가용한 자원에 따라 애플리케이션 성능에 영향을 줄 수 있음
  • 제한된 접근
    • VM은 운영체제 위에서 동작하기 때문에, 특정 운영체제 레벨의 기능에 직접 접근하기 어려울 수 있음
    • 예를 들어, 시스템 하드웨어에 직접 접근해야 하는 애플리케이션에는 추가 작업이 필요할 수 있음
  • 배포 크기
    • VM을 사용하면 애플리케이션과 함께 VM도 함께 배포해야 하기 때문에 애플리케이션의 배포 크기가 커질 수 있음
    • 최신의 VM은 모듈화와 최적화 기술을 사용하여 이 문제를 완화시키고 있음


JVM이란?

  • 자바를 실행하기 위한 가상 기계
  • 운영체제에 종속받지 않고, CPU가 Java를 인식하고 실행할 수 있게 하는 가상 컴퓨터
  • 소프트웨어와 하드웨어 사이에서 중개자 역할을 수행하여 자바 애플리케이션을 다른 운영체제 또는 플랫폼에서 실행할 수 있도록 함

Java 애플리케이션 실행 과정

  1. 소스 코드 작성
    • Java 소스 코드, 즉 원시 코드(*.java)는 CPU가 인식하지 못하기 때문에 기계어로 컴파일을 해주어야 함
    • 이때, Java는 JVM이라는 가상 머신을 거쳐서 OS에 도달하기 때문에 OS가 인식할 수 있는 기계어로 바로 컴파일 되지 않음
      • 일반 애플리케이션의 코드는 OS만 거치고 하드웨어로 전달되지만, Java 애플리케이션은 중간에 JVM을 거침
  2. Java compiler*.java 파일을 *.class로 변환함
    • 클래스 파일은 JVM이 인식할 수 있는 자바 바이트 코드 파일임
    • Java compiler는 JDK를 설치하면 javac.exe라는 실행 파일 형태로 존재함
      • javac 명령어를 통해 *.java*.class로 컴파일 할 수 있음
  3. JVM은 클래스 파일의 바이트 코드를 해석하여 기계어(Binary Code)로 변환하고 프로그램을 수행함
    • 변환된 바이트코드는 기계어가 아니기 때문에 OS에서 바로 실행되지 않음
      • JVM이 해당 바이트코드를 OS가 이해할 수 있도록 해석하는 역할을 담당함
    • 결국 JVM은 하드웨어/OS 사이 중간에서 하드웨어/OS 환경에 맞게 바이트코드를 기계어(Binary Code)로 변환하는 역할을 함
    • Java 애플리케이션은 JVM하고만 상호작용 하기 때문에 OS가 달라지더라도 프로그램 변경없이 실행이 가능함
      • OS에 종속적이지 않고, Java 파일 하나만 만들면 어느 디바이스든 JVM 위에서 실행할 수 있음

바이트코드: 특정 하드웨어가 아닌 가상 머신에서 돌아가는 실행 프로그램을 위한 이진 표현법



JVM 구조

동작 방식

  1. 자바 프로그램을 실행하면 JVM은 OS로부터 메모리를 할당받음
  2. 자바 컴파일러(javac)가 자바 소스코드(*.java)를 자바 바이트 코드(*.class)로 컴파일 함
  3. Class Loader는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크 하여 Runtime Data Area(실질적인 메모리를 할당 받아 관리하는 영역)에 올림
  4. Runtime Data Area에 로딩된 바이트 코드는 Execution Engine을 통해 해석됨
  5. 이 과정에서 Execution Engine에 의해 Garbage Collector의 작동과 Thread 동기화가 이루어짐

구성 요소

  • 클래스 로더(Class Loader)
  • 실행 엔진(Execution Engine)
    • 인터프리터(Interpreter)
    • JIT 컴파일러(Just-in-Time)
    • 가비지 콜렉터(Garbage collector)
  • 런타임 데이터 영역 (Runtime Data Area)
    • 메소드 영역
    • 힙 영역
    • PC Register
    • 스택 영역
    • 네이티브 메소드
  • JNI - 네이티브 메소드 인터페이스 (Native Medthod Interface)
  • 네이티브 메소드 라이브러리 (Native Method Library)

◽ 클래스 로더(Class Loader)

자바는 런타임에 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 특징이 있음
이 동적 로드를 담당하는 부분이 JVM의 클래스 로더

  • JVM 내로 클래스 파일(*.class)을 동적으로 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈
    • 즉, 로드된 바이트 코드(*.class)들을 엮어서 JVM의 메모리 영역인 Runtime Data Areas에 배치함
  • 클래스 파일의 로딩 과정 3단계(LoadingLinkingInitialization)
    • Loading(로딩): 클래스 파일을 가져와서 JVM의 메모리에 로드함
    • Linking(링크): 클래스 파일을 사용하기 위해 검증하는 과정
      • Verifying(검증) : .class 파일 형식이 유효한지 체크함
      • preparing(준비) : 클래스가 필요로 하는 메모리를 할당함
      • Resolving(분석) : 심볼릭 메모리 레퍼런스를 메모리 영역에 있는 실제 레퍼런스로 교체함
    • Initialization(초기화) : 클래스 변수들을 적절한 값으로 초기화함(static 변수 값 할당 등)

◽ 실행 엔진(Execution Engine)

  • 클래스 로더에 의해 Runtime Data Area에 적재된 바이트 코드들을 기계어로 변경해 명령어 단위로 실행하는 역할
    • 자바 바이트 코드(*.class)는 기계가 바로 수행할 수 있는 언어보다는 가상 머신이 이해할 수 있는 중간 레벨로 컴파일된 코드임
    • 따라서 실행 엔진은 이와 같은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경해줌
  • 위 수행과정에서 실행 엔지은 두 가지 방식을 혼합하여 바이트코드(명령어)를 실행함
    • 인터프리터(Interpreter)
      • 바이트 코드 명령어를 하나씩 읽어서 해석하고 바로 실행함
      • JVM 안에서 바이트 코드는 기본적으로 인터프리터 방식으로 동작함
      • 다만 같은 메서드라도 여러 번 호출되면 매번 해석하고 수행해야 하기 때문에 전체적인 속도가 느림
    • JIT(Just-In-Time) 컴파일러
      • Interpreter의 단점을 보완하기 위해 도입된 방식
      • 반복되는 코드를 발견하여 바이트 코드 전체를 컴파일하여 Native Code로 변경하고, 이후에는 해당 메서드를 더 이상 인터프리팅하지 않고 캐싱해두었다가 네이티브 코드로 직접 실행하는 방식
      • 하니씩 인터프리팅하여 실행하는 것이 아닌, 컴파일된 네이티브 코드를 실행하기 때문에 전체적인 실행 속도는 인터프리팅 방식보다 빠름
      • 하지만 바이트 코드를 네이티브 코드로 변환하는데 비용이 소요되기 때문에 JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않음
        • 인터프리터 방식으로 사용하다가 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행하는 방식으로 진행함
  • 더 이상 참조되지 않는 객체를 모아서 정리하는 GC(Garbage Collector) (opens in a new tab) 존재
    • JVM은 Garbage Collector를 통해 자동화된 메모리 관리 기능을 제공함

네이티브 코드: JAVA에서 부모가 되는 C언어나, C++, 어셈블리어로 구성된 코드


◽ 런타임 데이터 영역 (Runtime Data Area)

  • JVM의 메모리 영역으로, 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역
  • 6가지 영역 존재
    • 스택(stack) 영역
      • 지역 변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값등이 생성되는 영역
      • 클래스 수준의 정보를 저장하고 공유하는 자원
    • PC Register
      • 쓰레드가 생성될 때마다 생성되는 영역으로, 현재 쓰레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역
      • 현재 수행 중인 JVM 명령의 주소를 가짐
    • 네이티브 메소드 스택 영역
      • 자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역으로, 보통 C/C++등의 코드를 수행하기 위한 스택(JNI)
      • 바이트코드가 아닌 실제 수행할 수있는 기계어로 작성된 프로그램을 실행시키는 영역
    • 힙(heap) 영역
      • new 키워드로 생성된 객체와 배열이 생성되는 영역
      • 메소드 영역에 로드된 클래스만 생성이 가능하고 Garbage Collector가 참조되지 않는 메모리를 확인/제거하는 영역
    • 메소드 영역
      • 모든 스레드가 공유하는 영역으로, JVM이 시작될 때 생성됨
      • JVM이 읽어들인 클래스나 필드, 메서드 정보 등 초기화되는 대상을 저장하는 공간
    • Runtime Constant Pool
      • 메소드 영역에 존재하는 별도의 관리 영역으로, 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 함

◽ JNI - 네이티브 메소드 인터페이스 (Native Medthod Interface)

  • JNI는 자바가 다른 언어로 만들어진 어플리케이션과 상호 작용할 수 있는 인터페이스 제공하는 프로그램
  • JNI는 JVM이 Native Method를 적재하고 수행할 수 있도록 함
  • 실질적으로 제대로 동작하는 언어는 C와 C++ 정도만 존재함

◽ 네이티브 메소드 라이브러리 (Native Method Library)

  • C, C++로 작성된 라이브러리를 칭함


JVM 기능

  • 자바 애플리케이션 실행 환경 제공
    • 자바 소스 코드(.java 파일)는 컴파일되어 바이트 코드(.class 파일)로 변환되고, JVM은 이 바이트 코드를 해석하고 실행함
  • 메모리 관리
    • 메모리 할당, 해제, 가비지 컬렉션 등의 작업을 처리하여 프로그래머가 명시적으로 메모리를 관리할 필요 없이 효율적인 메모리 사용이 가능하도록 함
  • 가상 머신 지원
    • JVM은 자바 애플리케이션을 다양한 운영체제와 하드웨어 플랫폼에서 실행할 수 있도록 함
    • JVM은 운영체제와 하드웨어에 종속적인 부분을 처리하고, 자바 애플리케이션이 동일한 방식으로 동작하도록 보장함
  • 보안 관리
    • 애플리케이션 실행 중에 액세스 제어, 클래스 로딩, 코드 검증 등의 작업을 수행하여 악의적인 동작을 방지하고 안전한 실행 환경을 제공함
  • 예외 처리
    • 예외는 프로그램 실행 중에 발생하는 오류나 예기치 않은 상황을 나타내며, JVM은 이러한 예외를 적절히 처리하고 애플리케이션의 비정상적인 종료를 방지함

JVM은 자바의 핵심 원칙 중 하나인 Write Once, Run Anywhere를 실현하는데 중요한 역할을 한다. 이는 자바 애플리케이션이 플랫폼에 독립적으로 작성되고, JVM을 통해 다양한 환경에서 실행될 수 있다는 의미이다. JVM은 자바의 이식성과 유연성을 보장하며, 자바가 널리 사용되는 이유 중 하나이다.



JVM 언어

  • JVM은 주로 자바 언어의 실행을 위해 설계되었지만, 다른 언어를 JVM 위에 올릴 수 있음
  • 이를 가능하게 하는 주요 요소는 JVM 언어 또는 JVM 기반 언어라고 불리는 언어들
    • 이러한 언어는 자바 가상 머신에서 실행되는 바이트 코드 형태로 컴파일되며, JVM이 해당 언어를 해석하고 실행할 수 있도록 함

JVM 언어 종류

  • Kotlin
    • 자바와 상호 운용이 가능한 정적 타입의 프로그래밍 언어
    • 안드로이드 애플리케이션 개발에도 널리 사용됨
  • Scala
    • 객체 지향 및 함수형 프로그래밍을 지원하는 다중 패러다임 언어
    • 대규모 애플리케이션 개발에 널리 사용되며, 스파크(Spark)와 같은 대용량 데이터 처리 프레임워크의 기본 언어로 사용되기도 함
  • Groovy
    • 자바와 유사한 문법을 가지며, 동적 타이핑을 지원하는 스크립트 언어
    • 자바와의 상호 운용성이 우수하며, Gradle 빌드 도구의 스크립팅 언어로도 널리 사용됨
  • 이외에도 JRuby(루비), Jython(파이썬), Clojure(클로저) 등의 다른 언어도 JVM 위에서 실행될 수 있음
    • JVM 언어를 사용하면 자바 플랫폼의 다양한 기능과 라이브러리를 활용하면서 다른 언어의 장점을 살릴 수 있음

➕ JVM 계열 언어를 일반적으로 컴파일해서 사용하는 것은 불가능함

  • JVM 언어는 주로 바이트 코드로 컴파일되어 JVM에서 실행됨
    • 이는 JVM 언어가 자바 가상 머신에서 동작하도록 설계된 특성 때문
    • 따라서 JVM 언어를 일반적인 컴파일 언어처럼 기계어로 직접 변환하여 다른 환경에서 실행하는 것은 일반적으로 지원되지 않음
  • 그러나 JVM 언어의 일부에서는 Ahead-of-Time (AOT) 컴파일러를 사용하여 바이트 코드를 네이티브 코드로 변환하는 기능을 제공하는 경우도 있음
    • 이러한 기능을 사용하면 JVM 언어로 작성된 애플리케이션을 특정 플랫폼에 네이티브 바이너리로 컴파일하여 실행할 수 있음
    • 예를 들어, GraalVM은 JVM 언어를 AOT 컴파일하여 네이티브 이미지로 변환하는 기능을 제공함
  • 또한, 일부 JVM 언어는 자바와의 상호 운용성을 위해 자바 언어로 변환될 수 있음
    • 이는 해당 언어의 소스 코드를 자바 소스 코드로 변환한 후, 자바 컴파일러를 사용하여 자바 바이트 코드로 컴파일하는 방식
    • 이렇게 변환된 자바 바이트 코드는 JVM에서 실행할 수 있음
  • 따라서 일반적으로 JVM 언어는 JVM에서 실행하기 위해 바이트 코드로 컴파일되며, 다른 플랫폼에서 직접 컴파일되어 실행되는 것은 일반적으로 지원되지 않음


JVM과 내부에서 실행되고 있는 프로그램의 관계

  • JVM은 자바 애플리케이션을 실행하기 위한 가상 머신
    • 자바 애플리케이션은 JVM 위에서 실행되며, 이러한 관계에서 부모 프로세스 - 자식 프로세스와 비슷한 관계를 가질 수 있음
  • 일반적으로 JVM은 운영체제에서 실행 중인 하나의 프로세스로 간주됨
    • JVM은 자체적으로 스레드, 메모리, 리소스 등을 관리하며, 이러한 자원을 사용하여 자바 애플리케이션을 실행함
    • 자바 애플리케이션은 JVM 내에서 동작하는 여러 개의 스레드로 실행되는데, 이 스레드들은 JVM의 자식 프로세스로 간주될 수 있음
  • 하지만 부모 프로세스 - 자식 프로세스 관계는 주로 운영체제 수준의 관점에서 사용되는 용어이며, JVM은 이러한 관계를 직접적으로 반영하지는 않음
    • JVM은 운영체제와는 독립적으로 동작하며, 자바 애플리케이션을 실행하는 데 필요한 내부 처리를 담당함
  • 따라서, JVM과 자바 애플리케이션 간의 관계를 부모 프로세스 - 자식 프로세스 관계로 설명하는 것은 일반적인 운영체제 용어와의 비유로 이해할 수 있지만, 엄밀한 용어적 정의는 아님
    • JVM은 자바 애플리케이션 실행을 위한 실행 환경을 제공하고, 자바 애플리케이션은 JVM 내에서 실행되는 독립적인 단위로 간주됨



Ref