본문 바로가기

개발/자바

자바가 엔진단에서 어떻게 동작할까

큰 흐름은 다음과 같다.

1

JIT 컴파일러 (Just-In Time Compiler)

인터프리터에서 읽은 코드를 또 읽으면서 생기는 부하 문제가 있다.
해결 수단으로 메서드를 다시 읽지 않고 JIT 호출을 통해 기계어를 바로 읽는다.

2

관련 기술인 JRockit 기준 스레드를 모니터링해 자주 사용되는 메서드의 기계어를 저장해놓는다.
자주 사용되는 메서드라 판별될 때 까진 인터프리터가 바이트코드를 한 줄씩 해석하는 문제가 있다.

즉, 재시작 등에서 발생하는 콜드 미스의 비용이 매우 크다.

네이티브 컴파일러와 차이?

C의 경우 .exe, .elf 같은 형태로 디스크에 저장되지만 Java의 JIT는 JVM의 메서드 영역에 저장된다. 즉, 휘발성이다.

3

메서드 영역에 어떤 형태로 저장됐는지는 관련 문서가 없어 알 수 없었다.

JIT 컴파일러의 장점은 런타임 시점에 시스템의 상태(하드웨어, 메모리)를 고려한 최적화된 기계어 코드를 생성한다.

 

따라서 2번의 최적화를 거치게 된다

1. 소스 코드 -> 바이트 코드

2. 바이트 코드 -> 네이티브 기계어

 

인터프리터에서도 이 역할을 할 수 있지 않나? 란 생각을 했다.
여기에 대한 공개된 문서가 없다보니 인터프리터가 변환한 기계어를 가지고 최적화를 수행한다고 추측한다.

바이트 코드가 JVM에 등록되는 과정

Class Loader, Linking, Loading 등 다양한 요소들이 있다.
모든 것을 정리하기엔 지엽이고 단순 번역에만 그쳐서 내 기준 필요해보이는 것만 정리한다.

4

메서드 영역

자바의 클래스 또는 인터페이스가 생성되기 위해 참조되는 공간

다시 말하면 런타임 과정에서 객체를 생성할 때, 참고하는 영역이다.

 

메서드 영역은 여러 개의 클래스 또는 인터페이스 영역으로 나뉜다.
각 영역은 .class 파일의 메타 데이터들을 참조 가능한 값과 매칭된다.

 

이 영역은 gc에 의해 소멸하지 않는다. 즉, JVM이 종료되기 전까지 상주한다.

상수 관리

소스코드 내에 선언된 상수들은 .class 파일 내에 constant pool 영역에 관리된다. JVM에 등록되면서 이 값은 runtime constant pool 영역에 관리돤다.

 

이 둘의 차이점은 다음과 같다.

constant pool: 현재 클래스를 기준으로 어느정도의 위치에 있는지(오프셋)
runtime constant pool: 실제 JVM의 어느 영역에 있는지(참조값)

 

런타임 때 생성되는 상수들은 heap 영역에 관리된다.
String 객체면 intern() 메소드를 통해 runtime constant pool에 등록해 메모리를 절약할 수 있다.

static 관리

JVM에 로드 시, 메서드 영역 내에 기본값(int: 0, float: 0.0, wrapper class: null)으로 초기화된다.
이후 메소드 또는 필드(변수)가 호출될 때, 원래 값으로 초기화된다.

런타임 과정이나 디버깅 과정에서 이런 부분으로 헷갈릴 수 있다 생각해 정리한다.

Reference

https://docs.oracle.com/javase/specs/index.html

 

Java SE Specifications

Java Language and Virtual Machine Specifications Java SE 21 Released September 2023 as JSR 396 The Java Language Specification, Java SE 21 Edition HTML | PDF Preview feature: String Templates Preview feature: Unnamed Classes and Instance main methods Previ

docs.oracle.com