: 자바 프로그램은 완전한 기계어가 아닌 중간 단계의 바이트코드(Bytecode)다. 따라서 운영체제가 자바 프로그램을 실행하기 위해선 바이트코드를 해석하고 실행할 수 있는 일종의 번역기가 필요한데 그것이 바로 JVM이다.
✋ 바이트코드란?
: 바이트코드는 어떤 플랫폼 및 운영체제에 독립적으로 실행될 수 있는 가상기계용 기계어 코드다. 따라서 JVM만 설치되어 있으면 어떤 운영체제라도 각 운영체제에 맞는 실행 파일로 변환하여 실행할 수 있다.
(1) 클래스 로더(Class Loader)
: 클래스 파일을 메모리에 적재하는 기능. 자바 프로그램을 실행하기 위해 바이트코드로 이루어진 클래스 파일(.class)을 JVM으로 로딩시키고, JVM은 바이트코드를 해석하여 실행 가능한 상태로 만든다.
✍ 바이트코드 검증 도구(Bytecode Verifier)
: 클래스파일이 클래스로더에 의해 메모리에 로드되면 Runtime Data Area에 배치되기 전에 바이트코드에 대해 검증 절차를 가지게 되는데, 주요 내용은 다음과 같다.
① 코드가 JVM이 명시한 내용과 일치한지 여부
② 메모리에 허가되지 않은 접근이 존재하는지 여부
③ Stack Over Flow 체크
④ Data Types 체크
⇒ 데이터코드를 해석하고 검증하는 과정을 가지면서 프로그램의 접근을 제한하고 시스템 외부에 비정상적인 영향을 끼치는 것을 방지 (보안성 증가)참고자료
(2) 실행 데이터 영역(Runtime Data Area)
: 자바 프로그램을 실행하기 위해 OS에서 할당받은 메모리 공간. JVM은 이 영역에 자바 프로그램에서 사용하는 데이터들을 적재한다. 실행 데이터 영역은 크게 5가지로 나뉜다
✋ 상수 풀(Constant Pool)이란?
: 상수 자료형을 저장하고, 동일한 값의 중복을 막아 메모리 낭비를 방지하는 역할. 이미 있는 값이 들어오면 새로 만들지 않고 먼저 들어있던 같은 값의 주소를 같이 참조하게 한다.
(3) 실행 엔진(Execution Engine)
인터프리터(Interpreter)
: 바이트코드를 직접 한 줄씩 번역한 다음 바로 실행시키는 방법. 인터프리터를 실행시키면 해당 파일의 첫번째 줄을 읽어 기계어 명령어로 번역한 후 CPU에 바로 돌린다. 오류 발생 시 전체를 컴파일하지 않아도 돼서 디버깅에 편하지만, 컴파일 방식보다 시간 효율성이 떨어진다. (자바가 비교적 느리다고 하는 이유)
JIT 컴파일러(JIT Compiler)
: 인터프리터를 보완하기 위해 만들어진, 인터프리터와 컴파일러를 혼합한 방식. 실행 시점에서 인터프리트 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여 다음에 같은 코드가 있으면 번역하지 않고 캐싱해둔 값을 사용한다.
⇒ 매번 기계어 코드가 생성되는 것을 방지하여 인터프리터의 느린 실행 성능을 개선함
가비지 컬렉터(Garbage Collector)
: 메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역(=힙영역) 중 필요없게 된 영역을 해제하는 기능이다. 자바의 메모리 누수 현상(Memory Leak)을 방지한다.
가비지 컬렉션 기능 및 동작 문서 바로가기
(4) JNI (Native Method Interface) / Native Method Library
: 자바 외 언어(C, C++)로 작성된 코드를 자바에서 실행할 수 있도록 해주는 기능. Native Method Library를 이용해 C, C++의 함수를 호출하여 동작한다.
(1) JDK(Java Development Kit) : 자바 개발 도구
: 자바 기반 프로그램을 개발하기 위한 도구들로 이뤄진 패키지(컴파일러 + 디버거 + JRE 등 포함)
(2) JRE(Java Runtime Environment) : 자바 실행 환경
: 자바 프로그램을 실행하기 위한 도구들로 구성된 패키지(JVM + Class Library 등 포함)
(1) 자바 컴파일러(javac)가 자바 소스코드(.java)를 읽어들여 자바 바이트코드(.class)로 변환
(2) Class Loader를 통해 class 파일들을 JVM에 로딩
❗ 헷갈리는 개념 짚고 넘어가기
그림 참고 자료
JVM은 자바 프로그램을 실행하기 위한 '프로그램'이기 때문에 JVM 실행 시 메모리(RAM)에 적재된다. 또한, class 파일을 JVM에 로딩하기 위해서는 보조기억장치(하드디스크)에 저장되어 있던 class 파일을 메모리(RAM)에 불러와야 한다.
⇒ 즉, Class Loader 실행을 위해선 (1) JVM을 실행, (2) class 파일을 RAM에 적재, 이 두 가지가 수행되어야 한다.
(3) 로딩된 class 파일들은 실행엔진(Execution engine)을 통해 기계어로 해석
(4) 해석된 코드는 실행 데이터 영역(Runtime Data Area)에 배치되어 실질적인 수행이 이루어짐