컴퓨터가 어떤 프로그램을 실행하려면, 그 프로그램이 컴퓨터의 언어 (기계어)로 작성되어야 한다.
하지만 우리는 복잡하고 해석하기 어려운 기계어를 직접 작성하기보다는 코드를 작성한다.
바로 우리가 작성한 코드(여기서는 Java)를 컴퓨터가 이해할 수 있는 기계어로 변환해주는 역할과
어떤 운영체제에서도 Java코드가 실행 될 수 있도록 해주는 것이 바로 JVM이다.
JVM 작동 원리
JVM대해 파헤쳐보기 전에 우선 작동원리를 파악해보자.
1. 코드 작성
Java로 코드를 작성한다.
작성한 Java 코드는 .java 파일로 저장된다.
2. 컴파일
.java파일이 자바 컴파일러(javac)에 의해 .class 파일로 변환된다.
이 .class 파일은 자바 코드가 바이트코드로 변환되어 있다.
바이트 코드란 JVM에서 작동하도록 만든 이진 코드이다.
즉, JVM이 이해할 수 있는 언어로 변환된 코드이며 명령어의 크기가 1 바이트라서 자바 바이트 코드라고 불리고, 자바 코드를 배포하는 가장 작은 단위이다.
(참고로 c/c++의 경우에는 컴파일러가 기계어 코드로 변환해준다)
3. JVM 실행
.class 파일의 바이트코드를 읽고, 이를 각각의 컴퓨터가 이해할 수 있는 기계어로 변환하여 실행한다.
JVM 구조
JVM이 어떻게 처음에 설명한 역할을 수행할 수 있는 걸까?
JVM의 구조는 크게 GC(Garbage Collector), Execution Engine, Class Loader, Runtime Data Area 4가지로 이뤄져있다.
각각의 특징을 살펴보자.
GC(Garbage Collector)
힙 메모리 영역에 생성된 객체들 중 더 이상 참조되지 않는 객체를 자동으로 검색해 제거한다.
실행 엔진(Execution Engine)
메모리에 적재된 바이트코드(.class)를 기계어로 변환해 명령어 단위로 읽어 실행하는 역할을 한다.
위와 같은 역할을 수행하기 위해 2가지 방식이 사용된다.
인터프리터
바이트코드(.class)를 한 줄씩 읽어서 실행한다.
하지만 같은 코드를 실행할 때마다 바이크코드를 매번 해석해야해 속도가 느리다는 단점이 있다.
JIT(Just In Time Compiler) 컴파일러
인터프리터의 단점을 보완하기 위해 도입된 것으로
프로그램 실행 중에 바이트코드 전체 또는 일부를 네이티브 코드로 컴파일하고, 직접 실행한다.
이 방식은 초기 컴파일에는 시간이 걸리지만, 한 번 컴파일된 코드는 매우 빠르게 실행된다.
또한 JIT 컴파일러는 자주 실행되는 코드를 분석해 우선적으로 컴파일하여 성능을 최적화한다.
설명만 봤을 때는 JIT 컴파일러만 사용할 것 처럼 보인다.
하지만 JVM 실행 엔진에서는 이 두가지 방식을 함께 사용한다.
프로그램 실행 초기에는 인터프리터 방식으로 빠르게 시작하고 실행 중에 JIT 컴파일러가 분석을 통해 성능이 중요한 부분을 식별해 네이티브 코드로 컴파일한다.
클래스 로더(Class Loader)
자바는 컴파일 타임이 아니라 런타임(.class 파일을 실행할 때)시점, 즉 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하며 초초기화한다. (동적 로딩)
이 동적 로딩을 담당하는 것이 바로 클래스 로더이다.
런타임 데이터 영역(Runtime Data Areas)
JVM이 운영 체제 위에서 실행될 때, 할당 받는 메모리 영역으로 다음과 같이 분류된다.
Method 영역과 Heap 영역은 모든 쓰레드에서 공유되고, 나머지 영역은 쓰레드마다 각각 존재한다.
Method 영역
모든 스레드가 공유하는 영역으로, 클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메소드와 변수 정보 등)를 저장한다.
Heap 영역
모든 스레드가 공유하는 영역으로, 생성된 모든 객체와 배열이 저장되는 영역이다.
Stack 영역과 다르게 보관되는 메모리가 호출이 끝나더라도 삭제되지 않고 유지 된다.
그러다 GC가 더 이상 참조하지 않는 인스턴스가 있다면 제거한다.
Stack 영역
스레드마다 별도로 존재하며, 스레드의 메소드 호출과 로컬 변수 등을 관리한다.
PC Resgister 영역
스레드마다 별도로 존재하며, 스레드가 시작될 때 생성된다.
현재 실행 중인 JVM 명령의 주소값을 가진다.
Native Method Stack 영역
자바가 아닌 언어로 작성된 네이티브 코드를 위한 메모리 영역이다.
해당 영역의 메모리에 대한 설명은 아래 포스팅에서 더 자세하게 다룬다.
JVM 구동 방식
위 에서 설명한 JVM 구동 방식을 간단히 정리하면 다음과 같다.
'◼ JAVA' 카테고리의 다른 글
[Java] JVM 메모리 구조 파헤쳐 보기 (Static, Stack, Heap) (1) | 2024.04.14 |
---|---|
[엘레강스 오브젝트] "생성자에 코드를 넣지 마세요" 내용에 대한 생각 (1) | 2024.04.11 |
[Java] 개행 문자 사용시 주의점 (OS별 개행문자 통일하는 법) (1) | 2024.04.11 |
[Java] 함수 파라미터에 final 키워드를 꼭 붙여야 할까? (0) | 2024.04.11 |
[Java] 함수형 인터페이스란? 활용 방법에 대해 알아보자 (28) | 2023.11.07 |