저우즈밍 님의 "JVM 밑바닥까지 파헤치기" 책을 정리한 포스팅 입니다
1. 클래스 동적 로딩
- JVM 프로그램 실행 도중, 해당 클래스가 처음 필요해지는 시점에 클래스를 메모리에 로드하는 방식
- ✅ Lazy Loading 기반
장점
| 항목 | 설명 | 특징 |
| 확장성 | 필요할 때만 클래스를 로딩 | 모듈화 및 유연한 설계 가능 |
| 유연성 |
특정 클래스나 기능을 런타임 중에 교체/추가 가능
|
OSGi, WAS, 핫스왑 구조
|
| 외부 클래스 로딩 |
네트워크/파일시스템 등에서 외부 .class를 로드할 수 있음
|
|
| 런타임 바인딩 |
컴파일 타임이 아닌 실행 시 클래스 결정 가능
|
플러그인 구조에 유리
|
예시) OSGi
더보기
- 자바에서 클래스 로드를 번들 단위로 분리해서 설치/업데이트/제거/재시작을 런타임 중에 할 수 있게 만든 프레임워크
- 번들마다 자기 전용 ClassLoader를 가짐
예시) HotSwap
더보기
- 핫 리로드: 실행중인 JVM에서 코드를 수정하고 바로 반영하는 기술
- 새로운 ClassLoader를 만들어서 그 로더로 클래스를 다시 로드
- 이전 로드는 참조를 끊어 GC 대상이 되게 함
단점
- 성능 떨어짐
클래스 로더
- .class 바이트코드를 읽어 메모리에 올리는 주체
- ✅ 계층형: Bootstrap - Extension, Application 으로 구성됨
- ✅ 직접 정의 가능: Application ClassLoader 를 직접 정의하여 클래스 로드를 할 수 있음
코드) Application ClassLoader 정의
더보기
ClassLoader의 findClass() 또는 loadClass() 오버라이딩
// 간단한 커스텀 로더: 지정한 디렉토리에서 .class 바이트를 읽어 defineClass
static class MyClassLoader extends ClassLoader {
private final Path baseDir;
MyClassLoader(Path baseDir, ClassLoader parent) {
super(parent); // ✅ 부모를 AppClassLoader로 둠
this.baseDir = baseDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// name: com.example.Hello -> com/example/Hello.class
Path classFile = baseDir.resolve(name.replace('.', '/') + ".class");
try {
byte[] bytes = Files.readAllBytes(classFile);
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
throw new ClassNotFoundException("Cannot load " + name + " from " + classFile, e);
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
ClassLoader app = ClassLoader.getSystemClassLoader(); // AppClassLoader
Path dir = Path.of("classes"); // 예: ./classes/com/example/Hello.class 가 있어야 함
ClassLoader my = new MyClassLoader(dir, app);
Class<?> hello = Class.forName("com.example.Hello", true, my);
System.out.println("Loaded by: " + hello.getClassLoader());
System.out.println("App loader: " + app);
}
}
2. 과정
- Loading: 클래스의 바이트코드를 읽어와 JVM 메모리에 로드하는 단계 (클래스 로더 사용)
- Linking: 로드된 클래스가 실제 실행될 수 있도록 JVM 내부 구조에 연결하는 과정
- (Initialization): 클래스의 정적 초기화 코드인 <clinit> 메서드를 실행하는 단계
특징) Loading
더보기
- Heap에 java.lang.Class 인스턴스 생성 (내부적으로 메타데이터를 참조하여 사용하도록 함)
- 배열 클래스는 클래스 로더가 아닌 JVM이 직접 생성 (동적 생성 구조)
과정) Linking
더보기
| 단계 | 설명 | 항목 |
| Verification | JVM의 안전성 보장을 위한 검증 단계 |
- 파일 형식: 매직 넘버 버전 번호, 상수 타입 유효성 등
- 메타데이터: 상위 클래스 존재 여부, 필드 이름 충돌 여부 - 바이트코드 유효성: 명령어 유효성, 형변환 적절성 - 접근 제어 검증: 심벌 참조 대상의 접근 권한 확인 |
| Preparation |
클래스 변수의 메모리 할당 및 초기화
|
|
| Resolution |
상수 풀의 심벌 참조를 직접 참조로 변환
|
|
특징) Initialization
더보기
| 항목 | 설명 | |
| 부모 클래스 우선 |
상위 클래스의 <clinit>이 먼저 실행되고, 그 다음에 현재 클래스의 <clinit> 실행
|
|
| 실행 조건 | 아래 조건 중 하나라도 발생 시 실행됨 | - new (인스턴스 생성) - getstatic, putstatic (정적 필드 접근/수정) - invokestatic (정적 메서드 호출) |
'Java' 카테고리의 다른 글
| [JVM 밑바닥까지 파헤치기] 7-3. 클래스 로딩 매커니즘: 자바 모듈 시스템 (3) | 2024.12.23 |
|---|---|
| [JVM 밑바닥까지 파헤치기] 7-2. 클래스 로딩 매커니즘: 클래스 로더 (0) | 2024.12.23 |
| [JVM 밑바닥까지 파헤치기] 6-2. 클래스 파일 구조: 바이트코드 명령어 (2) | 2024.12.22 |
| [JVM 밑바닥까지 파헤치기] 6-1. 클래스 파일 구조: 클래스 파일 (0) | 2024.12.22 |
| [JVM 밑바닥까지 파헤치기] 3-5. 가비지 컬렉터와 메모리 할당 전략: 저지연 가비지 컬렉터 (0) | 2024.12.21 |