2회차 - (2025.07.15 / JVM)
1. JVM 은 어떤 구성 요소를 가지고 있는가?
1.1 클래스 로더 시스템
클래스 로더 시스템은 기본적으로 지연(lazy) 로딩이 기본 동작이고, 바이트코드서 특정 클래스를 처음으로 사용할 때 이루어진다.
클래스 로더 시스템은 가상 머신의 메모리에 로드되는 걸 시작으로 아래의 과정을 거친다.
로딩 (Loading)
링킹 (Linking) -> 하위 과정은 반드시 순서를 치켜야 한다.
검증 (Verification)
준비 (Preperation)
해석 (Resolution)
초기화 (Initialization)
JDk 9 기준으로 부트스트랩 -> 플랫폼 -> 애플리케이션 클래스 로더를 순서로 사용하게 된다.
클래스 로딩 과정을 순서대로 설명을 해보자.
1.1.1 로딩 (Loading)
로딩은 말 그대로 가상 머신이 해당 클래스 정보를 Method 영역에 로드하고, 힙 영역에 java.lang.Class 객체가 생성되는 것을 말한다.
로딩 과정에서 중요한 것은 부모 위임 모델(parents delegation model) 을 이해하는 것이 중요하다. 부모 위임 모델은 다음의 과정을 거쳐서 클래스를 로딩하게 된다.
클래스 로딩을 요청받은 클래스 로더는 처음부터 클래스 자체를 로드하려 시도하지 않는다. 그 대신 수준에 맞는 상위 클래스 로더로 요청을 위임한다. 따라서 모든 로드 요청은 우선 최상위인 부트스트랩 클래스 로더에 넘겨진다.
상위 로더가 자신이 처리할 요청이 아니라고 판단하면, 즉 요청받은 클래스가 자신의 검색 범위에 없다면 비로서 하위 로더가 시도한다.
클래스 로더를 부모 위임 모델로 구성하면 자바 클래스들이 자연스럽게 클래스 로더의 계층 구조를 따르게 된다는 이점이 있다.
예를 들어, rt.jar 에 포함된
java.lang.Object클래스의 로딩은 어떤 클래스 로더에 요청하더라도 최상위인 부트스트랩 클래스 로더가 처리한다. 즉, 프로그램이 아무리 많은 클래스 로더를 사용하더라도Object클래스는 모두 동일한 클래스임이 보장된다.
부모 위임 모델을 따르지 않으면 각 클래스 로더가 자체적으로 로드를 수행한다. 사용자가 직접 java.lang.Object 라는 이름의 클래스를 작성하여 클래스패스에 넣어 버리면 서로 다른 Object 클래스들이 생겨나 버린다. 그러면 자바 타입 시스템에서 가장 기본이 되는 동작들을 보장받지 못해 애플리케이션이 엉망이 될 것이다.
이런 시스템 불안정을 초래하는 보안적 이슈가 크기 때문에 부모 위임 모델을 사용한다!
1.1.2 링킹 (Linking)
링킹은 로드된 클래스 파일의 바이너리 데이터를 실행 가능한 상태로 만드는 단계를 의미하고 아래 순서대로 이루어져야 한다.
검증
준비
해석
1.1.2.1 검증 (Verification)
검증은 링킹 과정 중 첫번째 단계로 검증의 목적은 다음 2가지이다.
클래스 파일의 바이트 스트림에 담긴 정보가 "자바 가상 머신 명세" 에서 규정한 모든 제약을 만족하는지 확인한다.
이 정보를 코드로 변환해 실행했을 때 자바 가상 머신 자체의 보안을 위협하지 않는지 확인한다.
JVM 은 바이트코드를 실행하는 도중에 물리적으로 바이트코드를 수정할 수 있다. 때문에, 바이트코드를 검증하지 않는다면 오류가 있거나 악의적으로 작성된 바이트코드가 실행되어 프로그램 전체를 해칠 수 있다.
검증 단계는 자바 가상 머신이 악성 코드로부터 자신을 보호하기 위해서 엄격하게 진행해야 하며, 코드의 양적 측면과 실행 성능 측면에서 클래스 로딩 과정 중 매우 큰 비중을 차지한다.
결국 바이트 코드 검증은 결국 자바 가상 머신이 스스로를 보호하기 위한 필수 조치인 셈이다.
1.1.2.2 준비 (Preperation)
준비는 클래스 변수(정적 변수) 를 메모리에 할당하고 초기값(타입에 따라서 초기값은 달라질 수 있다)을 설정하는 단계이다.
1.1.2.3 해석 (Resolution)
해석은 자바 가상 머신이 바이트 코드 내 심벌 참조를 JVM 이 직접 사용할 수 있는 직접 참조로 바꾸기 위한 과정이다.
해석의 주된 대상은 클래스 파일 내의 상수 풀에 저장된 심볼릿 참조들이다. 해당 과정을 통해서 상수 풀의 데이터를 캐시한 것처럼 사용할 수 있는 것이다.
1.1.3 초기화 (Initialization)
클래스 로딩의 마지막 단계로 정적 변수, 정적 블럭을 초기화 한다.
이 과정까지 마무리하면 클래스 파일을 JVM 이 사용할 준비가 완료된 것이다.
1.2 런타임 메모리
1.2.1 프로그램 카운터(PC Register)
프로그램 카운터 레지스터는 작은 메모리 영역으로, 현재 실행 중인 스레드의 '바이트코드 줄 번호 표시기' 라고 생각하면 쉽다. 자바 가상 머신의 개념 모형(가상 머신의 '일반적인' 형태) 에서, 바이트코드 인터프리터는 이 카운터의 값을 바꿔 다음에 실행할 바이트코드 명령어를 선택하는 식으로 동작한다.
자바 가상 머신에서의 멀티스레딩은 CPU 코어를 여러 스레드가 교대로 사용하는 방식으로 구현되기 때문에 특정 시각에 각 코어는 한 스레드의 명령어만 실행하게 된다. 따라서 스레드 전환 후 이전에 실행하다 멈춘 지점을 정확하게 복원하려면 스레드 각각에는 고유한 프로그램 카운터가 필요하다. 따라서 각 스레드의 카운터는 서로 영향을 주지 않는 독립된 영역에 저장된다. (스레드 프라이빗 : 스레드 별로 저장)
1.2.2 자바 가상 머신 스택(JVM Stack)
프로그램 카운터처럼 자바 가상 머신 스택도 '스레드 프라이빗' 하며, 연결된 스레드와 운명을 같이 한다.
가상 머신 스택은 자바 메서드를 실행하는 스레드의 메모리 모델을 설명해준다. 각 메서드가 호출될 때마다 자바 가상 머신은 스택 프레임(Stack Frame) 을 만들어 지역 변수 테이블, 피연산자 스택, 메서드 반환값 등의 정보를 저장한다. 그리고 스택 프레임을 가상 머신 스택에 푸시(push) 메서드가 끝나면 팝(pop) 하는 일을 반복한다.
지역 변수 테이블에는 자바 가상 머신이 컴파일타임에 알 수 있는 다양한 기본 데이터 타입, 참조 타입, 반환 주소를 저장한다. 지역 변수 테이블을 구성하는 데 필요한 데이터 공간은 컴파일 과정에서 할당된다. 자바 메서드는 스택 프레임에서 지역 변수용으로 할당받아야 할 공간의 크기가 이미 완벽하게 결정되어 있다. 메서드 실행 중에는 절대 변하지 않는다.
1.2.3 네이티브 메서드 스택(Native Method Stack)
네이티브 메서드 스택은 가상 머신 스택과 매우 비슷한 역할을 한다. 차이점이라면 가상 머신 스택은 자바 메서드(바이트코드) 를 실행할 때 사용하고, 네이비트 메서드 스택은 네이티브 메서드를 실행할 때 사용한다는 것이다.
1.2.4 힙(Heap)
자바 힙은 자바 애플리케이션이 사용할 수 있는 가장 큰 메모리이다. 자바 힙은 모든 스레드가 공유하며 가상 머신이 구동될 때 만들어진다. 이 메모리 영역의 유일한 목적은 객체 인스턴스를 저장하는 것이고, 자바 세계의 '거의' 모든 객체 인스턴스가 이 영역에 할당된다.
1.2.5 메서드 영역(Method Area)
메서드 영역도 자바 힙처럼 모든 스레드가 공유한다. 메서드 영역은 가상 머신이 읽어 들인 타입 정보(클래스 수준의 정보), 상수, 정적 변수 그리고 JIT 컴파일러가 컴파일한 코드 캐시 등을 저장하는데 사용된다.
메서드 영역은 JDK 8 이전에는 Permanent Generation 을 사용했었다. 해당 구현체는 고정된 메모리 영역을 사용하기 때문에, 정해진 이상의 메모리를 사용하면 OOM 가 발생한다. 하지만 JDK 8 부터 나온 Metaspace 는 네이티브 메모리를 사용하면서 메모리가 부족할 때 운영체제에 의해서 메모리를 유연하게 사용할 수 있게 되었다. 때문에, JVM 입장에서는 거의 무한으로 사용하는 것처럼 느껴질 수 있다.
1.2.6 런타임 상수 풀(Runtime Constant Pool)
런타임 상수 풀은 메서드 영역의 일부다. 상수 풀 테이블에는 클래스 버전, 필드, 메서드, 인터페이스 등 클래스 파일에 포함된 설명 정보에 더해 컴파일 타임에 생성된 다양한 리터럴과 심벌 참조가 저장된다. 가상 머신이 클래스를 로드할 때 이러한 정보를 메서드 영역의 런타임 상수 풀에 저장한다.
문자열 상수 풀의 경우 힙 영역에 저장된다.
1.3 실행 엔진
1.3.1 인터프리터
1.3.2 JIT 컴파일러
계층형 컴파일 개념을 기본으로 하고 있다.
C1 : 낮은 최적화, 빠른 로딩
C2 : 높은 최적화, 늦은 로딩
1.3.3 GC
다음주에..
2. JVM 이 메모리를 어떤 식으로 구분하고 있는가?
1번 답변으로 대체
3. ClassLoader 란?
1번 답변으로 대체
4. class A는 어떤 식으로 로드되는가?
Last updated