Java Virtual Machine(JVM)
Last updated
Last updated
JDK(Java Development Kit)
자바 개발 도구로, JRE + 개발에 필요한 툴
자바 개발 환경으로 자바 어플리케이션을 개발하기 위해 필요한 도구를 제공함
자바 언어를 바이트 코드로 컴파일 해주는 자바 컴파일러(javac), 자바 클래스 파일을 해석해주는 역 어셈블리어(javap) 등 존재
JRE(Java Runtime Environment)
자바 실행 환경으로, 자바 애플리케이션을 실행할 수 있도록 구성된 배포판
JVM과 핵심 라이브러리 및 자바 어플리케이션 실행에 필요한 파일들을 포함함
JVM(Java Virtual Machine)
자바 어플리케이션을 실행하는 가상 머신
가상머신: 프로그램을 실행하기 위해 물리적 머신과 유사한 머신을 소프트웨어로 구현한 것
이식성
플랫폼과 운영체제에 독립적으로 동작할 수 있도록 함
개발한 애플리케이션은 VM 위에서 실행되므로, 어떤 운영체제나 플랫폼에서도 동일하게 작동할 수 있음
이식성이 높아져서 애플리케이션을 다양한 환경에서 실행하거나 배포하는 데 유리함
보안
보안을 강화할 수 있는 장치를 제공
VM은 코드 실행과 메모리 관리를 감독하며, 액세스 제어와 코드 검증 등의 보안 기능을 제공함
애플리케이션을 격리하여 악성 코드의 실행을 방지하고, 안전한 실행 환경을 제공함
자원 관리
메모리 할당, 가비지 컬렉션, 스레드 관리 등을 자동으로 처리하여 개발자가 직접 관리해야 하는 부담을 줄여줌
이를 통해 안정성과 성능을 향상시칼 수 있음
다중 언어 지원
JVM은 Java뿐만 아니라 Kotlin, Scala, Groovy 등 다른 JVM 언어들을 실행할 수 있음
이는 개발자에게 언어 선택의 자유를 주고, 기존 언어와 새로운 언어 간의 상호 운용성을 제공함
성능 오버헤드
VM은 어플리케이션 실행을 위해 추가적인 추상화 계층을 도입하므로 성능에 일정한 오버헤드가 발생함
일부 언어나 애플리케이션에서는 이 오버헤드가 큰 문제가 될 수 있음
최근 VM은 최적화 기술을 통해 이러한 성능 문제를 완화하고 있음
자원 사용
VM은 실행을 위해 일정한 시스템 자원이 필요함
애플리케이션을 실행할 때 이에 대한 고려가 필요하며, 가용한 자원에 따라 애플리케이션 성능에 영향을 줄 수 있음
제한된 접근
VM은 운영체제 위에서 동작하기 때문에, 특정 운영체제 레벨의 기능에 직접 접근하기 어려울 수 있음
예를 들어, 시스템 하드웨어에 직접 접근해야 하는 애플리케이션에는 추가 작업이 필요할 수 있음
배포 크기
VM을 사용하면 애플리케이션과 함께 VM도 함께 배포해야 하기 때문에 애플리케이션의 배포 크기가 커질 수 있음
최신의 VM은 모듈화와 최적화 기술을 사용하여 이 문제를 완화시키고 있음
자바를 실행하기 위한 가상 기계
운영체제에 종속받지 않고, CPU가 Java를 인식하고 실행할 수 있게 하는 가상 컴퓨터
소프트웨어와 하드웨어 사이에서 중개자 역할을 수행하여 자바 애플리케이션을 다른 운영체제 또는 플랫폼에서 실행할 수 있도록 함
소스 코드 작성
Java 소스 코드, 즉 원시 코드(*.java
)는 CPU가 인식하지 못하기 때문에 기계어로 컴파일을 해주어야 함
이때, Java는 JVM이라는 가상 머신을 거쳐서 OS에 도달하기 때문에 OS가 인식할 수 있는 기계어로 바로 컴파일 되지 않음
일반 애플리케이션의 코드는 OS만 거치고 하드웨어로 전달되지만, Java 애플리케이션은 중간에 JVM을 거침
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 위에서 실행할 수 있음
바이트코드: 특정 하드웨어가 아닌 가상 머신에서 돌아가는 실행 프로그램을 위한 이진 표현법
자바 프로그램을 실행하면 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)
자바는 런타임에 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 특징이 있음 이 동적 로드를 담당하는 부분이 JVM의 클래스 로더
JVM 내로 클래스 파일(*.class
)을 동적으로 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈
즉, 로드된 바이트 코드(*.class
)들을 엮어서 JVM의 메모리 영역인 Runtime Data Areas에 배치함
클래스 파일의 로딩 과정 3단계(Loading
→ Linking
→ Initialization
)
Loading(로딩)
: 클래스 파일을 가져와서 JVM의 메모리에 로드함
Linking(링크)
: 클래스 파일을 사용하기 위해 검증하는 과정
Verifying(검증) : .class
파일 형식이 유효한지 체크함
preparing(준비) : 클래스가 필요로 하는 메모리를 할당함
Resolving(분석) : 심볼릭 메모리 레퍼런스를 메모리 영역에 있는 실제 레퍼런스로 교체함
Initialization(초기화)
: 클래스 변수들을 적절한 값으로 초기화함(static 변수 값 할당 등)
클래스 로더에 의해 Runtime Data Area에 적재된 바이트 코드들을 기계어로 변경해 명령어 단위로 실행하는 역할
자바 바이트 코드(*.class)는 기계가 바로 수행할 수 있는 언어보다는 가상 머신이 이해할 수 있는 중간 레벨로 컴파일된 코드임
따라서 실행 엔진은 이와 같은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경해줌
위 수행과정에서 실행 엔지은 두 가지 방식을 혼합하여 바이트코드(명령어)를 실행함
인터프리터(Interpreter)
바이트 코드 명령어를 하나씩 읽어서 해석하고 바로 실행함
JVM 안에서 바이트 코드는 기본적으로 인터프리터 방식으로 동작함
다만 같은 메서드라도 여러 번 호출되면 매번 해석하고 수행해야 하기 때문에 전체적인 속도가 느림
JIT(Just-In-Time) 컴파일러
Interpreter의 단점을 보완하기 위해 도입된 방식
반복되는 코드를 발견하여 바이트 코드 전체를 컴파일하여 Native Code로 변경하고, 이후에는 해당 메서드를 더 이상 인터프리팅하지 않고 캐싱해두었다가 네이티브 코드로 직접 실행하는 방식
하니씩 인터프리팅하여 실행하는 것이 아닌, 컴파일된 네이티브 코드를 실행하기 때문에 전체적인 실행 속도는 인터프리팅 방식보다 빠름
하지만 바이트 코드를 네이티브 코드로 변환하는데 비용이 소요되기 때문에 JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않음
인터프리터 방식으로 사용하다가 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행하는 방식으로 진행함
더 이상 참조되지 않는 객체를 모아서 정리하는 GC(Garbage Collector) 존재
JVM은 Garbage Collector를 통해 자동화된 메모리 관리 기능을 제공함
네이티브 코드: JAVA에서 부모가 되는 C언어나, C++, 어셈블리어로 구성된 코드
JVM의 메모리 영역으로, 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역
6가지 영역 존재
스택(stack) 영역
지역 변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값등이 생성되는 영역
클래스 수준의 정보를 저장하고 공유하는 자원
PC Register
쓰레드가 생성될 때마다 생성되는 영역으로, 현재 쓰레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역
현재 수행 중인 JVM 명령의 주소를 가짐
네이티브 메소드 스택 영역
자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역으로, 보통 C/C++등의 코드를 수행하기 위한 스택(JNI
)
바이트코드가 아닌 실제 수행할 수있는 기계어로 작성된 프로그램을 실행시키는 영역
힙(heap) 영역
new
키워드로 생성된 객체와 배열이 생성되는 영역
메소드 영역에 로드된 클래스만 생성이 가능하고 Garbage Collector가 참조되지 않는 메모리를 확인/제거하는 영역
메소드 영역
모든 스레드가 공유하는 영역으로, JVM이 시작될 때 생성됨
JVM이 읽어들인 클래스나 필드, 메서드 정보 등 초기화되는 대상을 저장하는 공간
Runtime Constant Pool
메소드 영역에 존재하는 별도의 관리 영역으로, 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 함
JNI는 자바가 다른 언어로 만들어진 어플리케이션과 상호 작용할 수 있는 인터페이스 제공하는 프로그램
JNI는 JVM이 Native Method를 적재하고 수행할 수 있도록 함
실질적으로 제대로 동작하는 언어는 C와 C++ 정도만 존재함
C, C++로 작성된 라이브러리를 칭함
자바 애플리케이션 실행 환경 제공
자바 소스 코드(.java 파일)는 컴파일되어 바이트 코드(.class 파일)로 변환되고, JVM은 이 바이트 코드를 해석하고 실행함
메모리 관리
메모리 할당, 해제, 가비지 컬렉션 등의 작업을 처리하여 프로그래머가 명시적으로 메모리를 관리할 필요 없이 효율적인 메모리 사용이 가능하도록 함
가상 머신 지원
JVM은 자바 애플리케이션을 다양한 운영체제와 하드웨어 플랫폼에서 실행할 수 있도록 함
JVM은 운영체제와 하드웨어에 종속적인 부분을 처리하고, 자바 애플리케이션이 동일한 방식으로 동작하도록 보장함
보안 관리
애플리케이션 실행 중에 액세스 제어, 클래스 로딩, 코드 검증 등의 작업을 수행하여 악의적인 동작을 방지하고 안전한 실행 환경을 제공함
예외 처리
예외는 프로그램 실행 중에 발생하는 오류나 예기치 않은 상황을 나타내며, JVM은 이러한 예외를 적절히 처리하고 애플리케이션의 비정상적인 종료를 방지함
JVM은 자바의 핵심 원칙 중 하나인
Write Once, Run Anywhere
를 실현하는데 중요한 역할을 한다. 이는 자바 애플리케이션이 플랫폼에 독립적으로 작성되고, JVM을 통해 다양한 환경에서 실행될 수 있다는 의미이다. JVM은 자바의 이식성과 유연성을 보장하며, 자바가 널리 사용되는 이유 중 하나이다.
JVM은 주로 자바 언어의 실행을 위해 설계되었지만, 다른 언어를 JVM 위에 올릴 수 있음
이를 가능하게 하는 주요 요소는 JVM 언어
또는 JVM 기반 언어
라고 불리는 언어들
이러한 언어는 자바 가상 머신에서 실행되는 바이트 코드 형태로 컴파일되며, JVM이 해당 언어를 해석하고 실행할 수 있도록 함
Kotlin
자바와 상호 운용이 가능한 정적 타입의 프로그래밍 언어
안드로이드 애플리케이션 개발에도 널리 사용됨
Scala
객체 지향 및 함수형 프로그래밍을 지원하는 다중 패러다임 언어
대규모 애플리케이션 개발에 널리 사용되며, 스파크(Spark)와 같은 대용량 데이터 처리 프레임워크의 기본 언어로 사용되기도 함
Groovy
자바와 유사한 문법을 가지며, 동적 타이핑을 지원하는 스크립트 언어
자바와의 상호 운용성이 우수하며, Gradle 빌드 도구의 스크립팅 언어로도 널리 사용됨
이외에도 JRuby(루비), Jython(파이썬), Clojure(클로저) 등의 다른 언어도 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 내에서 실행되는 독립적인 단위로 간주됨