JVM(Java Virtual Mahcine)은 자바로 작성된 바이트 코드 파일을 실행시켜주는 가상 머신입니다.
자바는 개발 당시 각각의 다른 운영체제에서 동작시키는 언어를 목표로 개발했기에 OS가 바이트코드 파일을 직접 실행하지 않고 JVM이 작성된 파일을 해석하여 실행합니다. 그래서 자바 어플리케이션은 운영체제에 독립적으로 소스파일을 작성하여 어디서든 실행할 수 있습니다.
하지만 컴파일된 바이트코드 파일을 JVM 인터프리터가 OS에 한번 더 해석하는 과정이 필요하게 되어 low레벨의 다른 언어보다 수행 속도가 느리다는 단점을 가지며, 소스파일과 다르게 JVM 자체는 OS 종속적입니다.
자바는 컴파일 언어이므로 프로그램을 수행하기 위해서는 컴파일 과정을 거쳐 기계어로 변환된 클래스파일이 필요합니다. Java Compiler는 .java
파일을 .class
파일로 변환하는 동작을 수행합니다.
자바 소스 파일(.java
)은 자바 컴파일러에 의해 컴파일되고 JVM에 의해서 실행(class
)됩니다.
javac test.java
터미널 환경에서 javac
로 컴파일러를 호출하며 매개변수로 소스파일을 입력하게되면 .class
파일을 생성합니다
java test.class
java
명령으로 설치되어 있는 JRE
에 맞게 바이트코드를 수행합니다.
자바에서 클래스 파일(바이트 코드 파일)을 로딩할 때 작성된 모든 파일들을 한번에 로드하는 게 아닌 접근 시 필요한 클래스 파일들만 로딩(동적 로딩, Dynamic Loading)하게 됩니다. 이 과정은 클래스 로더가 수행하게되며 다음과 같은 과정을 거칩니다.
.class
파일은 각각에 패키지에 따로 정의되어 있으므로 classpath를 지정하여 JVM에게 검사해야할 파일의 위치를 알림로딩
런타임 시 클래스 로더가 classpath에 있는 클래스 파일들을 메모리에 올립니다. 이 때의 로딩 과정은 동적으로 수행되며 클래스 파일 내에 선언된 다른 클래스가 로딩되어 있지 않다면 그 클래스 먼저 로딩하는 과정을 반복합니다.
링킹
검증
로딩된 각 클래스는 적절성 검증을 하며 실패시 오류를 반환합니다.
준비
정적으로 선언된 속성의 메모리가 할당되며 각 정적 속성의 기본 값이 입력됩니다.
해석
정적 영역의 참조되지 않은 메모리들을 참조와 연결합니다
초기화
정적 속성이 명시된 값 및 초기화 블럭으로 작성된 값으로 정의됩니다.
클래스 로더로 해당 클래스 파일의 로드를 마치면 JVM은 두 가지 방법으로 변환하여 수행합니다. 클래스 파일의 바이트 코드도 컴파일된 언어지만 각 OS에 종속적인 기계어가 아니므로 JVM이 해석하여 실행할 필요가 있습니다.
클래스 파일을 명령어 단위로 한 줄씩 읽어서 수행합니다. 이 과정은 인터프리터 언어가 갖고 있는 단점과 마찬가지로 느린 수행 과정을 거칩니다.
JIT 컴파일러는 인터프리터의 단점을 보완하기 위해 도입되었습니다. 여러번 수행되는 중복코드를 native code
로 컴파일하여 중복 호출하지 않도록 합니다. 또한 native code
는 각 OS 에서 직접 수행할 수 있는 코드이며 캐시에 보관하므로 빠르게 수행됩니다.
하지만 한 번 더 컴파일 하는 과정의 소요시간이 길기 때문에 JVM 에서 해당 바이트 코드가 얼마나 자주 수행되는지 체크하여 JIT로 수행할 지 인터프리터로 수행할 지 결정합니다.
JVM은 자바 어플리케이션에서 사용되는 메모리를 모두 자동으로 관리해주는 Gabage Collector
가 존재합니다. 레퍼런스가 없는 동적으로 할당된 메모리를 자동으로 해제해주며 이를 개발자가 신경쓰지 않아도 되게 합니다. 이 과정이 있기에 개발하는 과정에서 개발자가 메모리에 접근하는 것을 불허하며 개발자가 메모리에 관리에 신경을 쓰지 않아도 되게합니다.
JVM 위의 모든 자바 어플리케이션은 하나의 JVM 프로세스 위에서 동작하며 쓰레드 단위로 독립된 메모리 공간을 사용하거나, JVM 내에 공유하는 메모리 공간을 갖고 있습니다.
JVM의 각 쓰레드별로 갖는 메모리 공간이며 쓰레드의 명령 및 JVM 의 명령 주소를 기록합니다.
메서드를 수행한 후 반환 시 메모리에서 빠져나가므로 이러한 메서드들의 호출 스택을 임시로 저장하기 위한 공간입니다. 각 쓰레드별로 별개의 호출 스택 영역을 갖습니다.
자바 내에는 JNI
라는 네이티브 메서드가 있는데 System
클래스에 있는 여러 메서드 처럼 JVM 에서 동작하는 게 아닌 미리 native code
로 작성된 메서드들을 뜻합니다. Native 메서드 스택은 JNI
메서드들을 위해 할당된 메모리 공간입니다.
동적으로 생성( 예 : new
)되는 모든 메모리가 적재되는 공간입니다. Garbage Collector는 이 영역에 있는 메모리들을 관리합니다.
클래스 로딩 시 클래스의 정보가 로딩되는 공간이며 Garbage Collector의 관리를 받습니다.
자바에서 사용되는 기본형 값(String 포함)들은 한번 작성 시 상수풀에 저장하여 다시 호출할 때 상수풀에서 값을 가져옵니다.
또한 각 클래스의 속성의 이름, 타입 접근제어자 및 리턴타입, 클래스의 부모 및 인터페이스 등의 정보를 저장합니다.
JDK(Java Development Kit)은 자바 개발을 위한 도구를 칭하는 말이며 JRE, 자바 컴파일러 등의 개발도구를 포함하고 있습니다.
JRE(Java Runtime Environment) 는 JVM의 실행 환경을 구성하며 .jar
파일과 같은 라이브러리 및 기타 실행 도구들을 포함합니다. JRE는 자바 실행 환경을 구성하는 것이며 개발과 관련된 도구는 포함되어 있지 않습니다.