출처 - 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 애플리케이션 실행 과정
- 소스 코드 작성
- Java 소스 코드, 즉 원시 코드(
*.java
)는 CPU가 인식하지 못하기 때문에 기계어로 컴파일을 해주어야 함 - 이때, Java는 JVM이라는 가상 머신을 거쳐서 OS에 도달하기 때문에 OS가 인식할 수 있는 기계어로 바로 컴파일 되지 않음
- 일반 애플리케이션의 코드는 OS만 거치고 하드웨어로 전달되지만, Java 애플리케이션은 중간에 JVM을 거침
- Java 소스 코드, 즉 원시 코드(
Java compiler
가*.java
파일을*.class
로 변환함- 클래스 파일은 JVM이 인식할 수 있는 자바 바이트 코드 파일임
- Java compiler는 JDK를 설치하면
javac.exe
라는 실행 파일 형태로 존재함javac
명령어를 통해*.java
를*.class
로 컴파일 할 수 있음
JVM
은 클래스 파일의바이트 코드
를 해석하여기계어(Binary Code)
로 변환하고 프로그램을 수행함- 변환된 바이트코드는 기계어가 아니기 때문에 OS에서 바로 실행되지 않음
- JVM이 해당 바이트코드를 OS가 이해할 수 있도록 해석하는 역할을 담당함
- 결국 JVM은 하드웨어/OS 사이 중간에서 하드웨어/OS 환경에 맞게 바이트코드를 기계어(Binary Code)로 변환하는 역할을 함
- Java 애플리케이션은 JVM하고만 상호작용 하기 때문에 OS가 달라지더라도 프로그램 변경없이 실행이 가능함
- OS에 종속적이지 않고, Java 파일 하나만 만들면 어느 디바이스든 JVM 위에서 실행할 수 있음
- 변환된 바이트코드는 기계어가 아니기 때문에 OS에서 바로 실행되지 않음
바이트코드: 특정 하드웨어가 아닌 가상 머신에서 돌아가는 실행 프로그램을 위한 이진 표현법
JVM 구조
동작 방식
- 자바 프로그램을 실행하면 JVM은 OS로부터 메모리를 할당받음
- 자바 컴파일러(
javac
)가 자바 소스코드(*.java
)를 자바 바이트 코드(*.class
)로 컴파일 함 Class Loader
는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크 하여Runtime Data Area
(실질적인 메모리를 할당 받아 관리하는 영역)에 올림Runtime Data Area
에 로딩된 바이트 코드는Execution Engine
을 통해 해석됨- 이 과정에서
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단계(
Loading
→Linking
→Initialization
)Loading(로딩)
: 클래스 파일을 가져와서 JVM의 메모리에 로드함Linking(링크)
: 클래스 파일을 사용하기 위해 검증하는 과정- Verifying(검증) :
.class
파일 형식이 유효한지 체크함 - preparing(준비) : 클래스가 필요로 하는 메모리를 할당함
- Resolving(분석) : 심볼릭 메모리 레퍼런스를 메모리 영역에 있는 실제 레퍼런스로 교체함
- Verifying(검증) :
Initialization(초기화)
: 클래스 변수들을 적절한 값으로 초기화함(static 변수 값 할당 등)
◽ 실행 엔진(Execution Engine)
- 클래스 로더에 의해 Runtime Data Area에 적재된 바이트 코드들을 기계어로 변경해 명령어 단위로 실행하는 역할
- 자바 바이트 코드(*.class)는 기계가 바로 수행할 수 있는 언어보다는 가상 머신이 이해할 수 있는 중간 레벨로 컴파일된 코드임
- 따라서 실행 엔진은 이와 같은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경해줌
- 위 수행과정에서 실행 엔지은 두 가지 방식을 혼합하여 바이트코드(명령어)를 실행함
- 인터프리터(Interpreter)
- 바이트 코드 명령어를 하나씩 읽어서 해석하고 바로 실행함
- JVM 안에서 바이트 코드는 기본적으로 인터프리터 방식으로 동작함
- 다만 같은 메서드라도 여러 번 호출되면 매번 해석하고 수행해야 하기 때문에 전체적인 속도가 느림
- JIT(Just-In-Time) 컴파일러
- Interpreter의 단점을 보완하기 위해 도입된 방식
- 반복되는 코드를 발견하여 바이트 코드 전체를 컴파일하여 Native Code로 변경하고, 이후에는 해당 메서드를 더 이상 인터프리팅하지 않고 캐싱해두었다가 네이티브 코드로 직접 실행하는 방식
- 하니씩 인터프리팅하여 실행하는 것이 아닌, 컴파일된 네이티브 코드를 실행하기 때문에 전체적인 실행 속도는 인터프리팅 방식보다 빠름
- 하지만 바이트 코드를 네이티브 코드로 변환하는데 비용이 소요되기 때문에 JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않음
- 인터프리터 방식으로 사용하다가 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행하는 방식으로 진행함
- 인터프리터(Interpreter)
- 더 이상 참조되지 않는 객체를 모아서 정리하는 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
) - 바이트코드가 아닌 실제 수행할 수있는 기계어로 작성된 프로그램을 실행시키는 영역
- 자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역으로, 보통 C/C++등의 코드를 수행하기 위한 스택(
- 힙(heap) 영역
new
키워드로 생성된 객체와 배열이 생성되는 영역- 메소드 영역에 로드된 클래스만 생성이 가능하고 Garbage Collector가 참조되지 않는 메모리를 확인/제거하는 영역
- 메소드 영역
- 모든 스레드가 공유하는 영역으로, JVM이 시작될 때 생성됨
- JVM이 읽어들인 클래스나 필드, 메서드 정보 등 초기화되는 대상을 저장하는 공간
- Runtime Constant Pool
- 메소드 영역에 존재하는 별도의 관리 영역으로, 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 함
- 스택(stack) 영역
◽ 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은 자바 애플리케이션 실행을 위한 실행 환경을 제공하고, 자바 애플리케이션은 JVM 내에서 실행되는 독립적인 단위로 간주됨
-