JVM이 .class 파일을 읽어서 메모리에 올리는 과정을 담당한다.
Bootstrap ClassLoader ← JVM 내장 (C/C++), java.lang.* 등 핵심 라이브러리
└─ Platform ClassLoader ← Java 9+, 확장 모듈 (java.sql.*, javax.*)
└─ Application ClassLoader ← 애플리케이션 클래스패스의 클래스들
클래스 로딩 요청이 오면 자신이 직접 찾기 전에 부모에게 먼저 위임한다.
Application ClassLoader가 java.lang.String 요청
→ Platform ClassLoader에 위임
→ Bootstrap ClassLoader에 위임
→ Bootstrap이 rt.jar에서 찾아서 로딩 성공
→ Application까지 내려오지 않음
덕분에 java.lang.String을 직접 만들어도 실제로 로딩되지 않아서 핵심 클래스를 변조할 수 없다.
.class 파일을 읽어서 바이너리 데이터를 만들고 메소드 영역에 저장한다.
저장 내용: FQCN(패키지 포함 클래스명), 클래스/인터페이스/Enum 구분, 메소드·변수 정보
로딩 완료 후 해당 클래스 타입의 Class 객체를 힙 영역에 생성한다.
세 단계로 구성된다.
.class 파일 형식이 JVM 스펙에 맞는지 검증static 변수를 코드에서 지정한 값으로 초기화하고, static 블록을 실행한다.
static int count = 10; // Prepare에서 0, 초기화에서 10으로 설정
static {
System.out.println("초기화 실행"); // 여기서 실행
}
자주 혼동되는 두 에러. 발생 시점이 다르다.
| ClassNotFoundException | NoClassDefFoundError | |
|---|---|---|
| 종류 | Checked Exception | Error |
| 발생 시점 | 런타임에 동적으로 클래스 로딩 시도 시 | 컴파일 시점에는 있었지만 런타임에 없을 때 |
| 원인 | Class.forName(), ClassLoader.loadClass() 실패 | 클래스패스 누락, JAR 배포 누락 |
// ClassNotFoundException 발생 예시
try {
Class.forName("com.example.NonExistentClass");
} catch (ClassNotFoundException e) {
// 동적 로딩 실패
}
// NoClassDefFoundError 발생 예시
// 컴파일 시에는 SomeClass가 있었지만
// 런타임에 해당 JAR가 없는 경우 → JVM이 Error를 던짐
SomeClass obj = new SomeClass();
웹 애플리케이션을 재배포(hot deploy)할 때 이전 클래스 로더가 GC되지 않으면 Metaspace OOM이 발생한다.
원인: ThreadLocal, static 필드 등에 이전 클래스 로더 참조가 남아있음
증상: 재배포를 반복하면 Metaspace 사용량이 계속 증가
해결: ThreadLocal.remove(), WeakReference 사용
Spring Boot는 기본적으로 Application ClassLoader를 사용하지만, @Configuration의 CGLIB 프록시 생성이나 @Transactional AOP 프록시는 동적 클래스 생성을 활용한다. 이때 내부적으로 바이트코드 조작 라이브러리(ASM, CGLIB)를 사용한다.