JVM과정을 상세화해서 나타낸 도식
Class Loader는 실행 시점에 .class 파일을 JVM 내로 동적으로 로드하고, 링크를 통해 런타임 데이터 영역 안의 Method Area에 배치하는 작업을 하는 모듈이다.
클래스 로더의 로딩 순서는 다음과 같이 3단계로 구성된다. (Loading -> Linking -> Initialization)

여기서 Linking은 3가지로 더 세분화 되는데 일단 Loading에 대해서 알아보자
Loading은 .class파일을 JVM 메모리로 가져오는 단계다.
이 단계에서 하는 일
클래스 로더가 .class 파일을 찾고 .class 파일의 바이트코드를 읽는다. 그리고 읽은 내용을 JVM의 Method Area에 저장하고 해당 클래스타입의 Class 객체를 생성해서 Heap 영역에 저장한다.
Linking는 클래스 파일을 사용하기 위해 검증하는 과정이고, 검증, 준비, 분석 이렇게 3가지의 세부 단계로 나뉜다.
verify (검증) - 로드된 클래스의 바이트코드가 JVM의 명세에 맞게 올바르게 되어 있는지 검증하는 과정이다.
perpare (준비) - 클래스나 인터페이스에 필요한 메모리를 할당하는 과정이다.
resolve (분석/해결) - 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체하는 과정이다.
Initialization는 링크 단계에서 확보한 메모리 영역에 클래스의 static 변수를 명시된 값으로 할당한다.

클래스 로더의 종류는 부트스트랩, 확장, 시스템(애플리케이션) 클래스 로더 크게 이렇게 3가지로 나눠져있다.
Bootstrap Class Loader는 최상위 클래스 로더다.
JVM이 실행될 때 가장 먼저 실행되고, 다른 클래스 로더와 다르게 자바가 아닌 네이티브 코드(C/C++)로 구현되어 있다.
로드 대상: 자바의 핵심 라이브러리 (java.lang.*, java.util.*, java.io.*등)
다른 모든 클래스 로더는 Bootstrap Class Loader가 로드한 클래스들을 기반으로 동작한다.
Extension Class Loader는 부트스트랩 클래스 로더의 자식이고, 표준 코어 자바 클래스 외의 확장 클래스를 로드한다.
Java 8 때는 URLClassLoader를 상속하고, jre/lib/ext 내 모든 클래스를 로드했다.
하지만 ext 디렉토리는 관리도 안 되고 보안도 취약 -> 모듈 개념과 맞지 않았다.
그래서 Java 9 이후부터는 Platform Loader로 변경되었고, BuiltinClassLoader를 상속한다.
로드 대상: Java SE 표준 모듈 (java.sql, java.xml, java.management 등)
Application Class Loader는 자바 프로그램 실행 시 지정한 Classpath에 있는 클래스 파일 or jar에 속한 클래스들을 로드한다.
로드 대상: 애플리케이션 코드 (내가 작성한 모든 클래스), 외부라이브러리 (Gradle / Maven 의존성)
JVM의 클래스 로더는 새로운 클래스를 로드해야할 때, 다음과 같이 동작한다.
Application Class Loader
↓
Platform Class Loader
↓
Bootstrap Class Loader
Bootstrap Class Loader는 자신의 클래스패스(JDK core lib)에 해당 클래스가 있는지 확인한다.
있으면 직접 로드하고, 없으면 Platform Class Loader에 요청을 넘긴다.
Platform Class Loader가 자신의 영역에서 탐색한다. 있으면 로드하고, 없으면 Application Class Loader에 요청을 넘긴다.
Application Class Loader가 classpath에서 탐색한다. 있으면 로드하고, 없으면 ClassNotFoundException이 발생한다.
클래스 로더의 종류는 위임, 가시성, 유일성 원칙 이렇게 3가지로 나눠져있다.
클래스 로더는 클래스 로딩 요청을 받으면, 자신이 직접 로딩하지 않고 먼저 부모 클래스 로더에게 위임한다.
ex) Application ClassLoader가 요청 받음 → Extension ClassLoader에게 위임
Extension ClassLoader가 로딩 시도 → 성공하면 반환, 실패하면 자식에게 다시 넘김
하위 클래스 로더는 상위 클래스 로더가 로딩한 클래스를 볼 수 있지만, 상위 클래스 로더는 하위 클래스 로더가 로딩한 클래스를 볼 수 없다.
ex)
// java.lang.String (Bootstrap이 로딩)
public class String {
// 이 클래스는 java.util.ArrayList를 사용할 수 있음
// why? - ArrayList도 Bootstrap이 로딩하기 때문에
}
// com.myapp.MyClass (Application이 로딩)
public class MyClass {
// 이 클래스는 String을 사용할 수 있음
// why? - String은 상위 클래스로더가 로딩했기 때문에
private String name;
}
JVM 내에서 하나의 클래스는 특정 클래스 로더에 의해 한 번만 로드될 수 있다.
(유일성 보장)
동적 클래스 로딩이란, JVM이 프로그램 시작 시 모든 클래스를 미리 로드하지 않고, 클래스가 실제로 필요해지는 시점에 로드하는 방식이다.
동적 클래스 로딩은 로드타임 동적 로딩, 런타임 동적 로딩 이렇게 2가지 방식이 있다.
먼저 로드타임 동적 로딩부터 알아보자
public class HelloWorld {
public static void main(String[] args) {
System.out.println("안녕하세요!");
}
}
위 코드의 경우 다음과 같이 동작한다.
JVM이 실행되고, 클래스 로딩이 시작되면 각 클래스가 로드되는 과정에서 필요한 경우 최상위 클래스인 java.lang.Object가 로드된다.
클래스 로더는 명령 행에서 지정한 HelloWorld 클래스를 로드하기 위해
HelloWorld.class 파일을 읽는다.
이후 main 메서드가 실행되는 과정에서 필요한 클래스인 java.lang.String과 java.lang.System이 필요한 시점에 추가로 로드된다.
이처럼 프로그램 실행 중, 코드의 실행 흐름에 따라 필요한 클래스를 그 시점에 로드하는 방식을 로드타임 동적 로딩이라고 한다.
public class RuntimeLoading {
public static void main(String[] args) {
try {
Class cls = Class.forName(args[0]);
Object obj = cls.newInstance();
Runnable r = (Runnable) obj;
r.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
위 코드에서 Class.forName(className)은 파라미터로 전달된 className에 해당하는 클래스를 런타임 시점에 로드하고 초기화한 뒤, 해당 클래스를 대표하는 Class 객체를 반환한다.
Class.forName() 메서드가 실행되기 전까지는 RuntimeLoading 클래스가 어떤 클래스를 동적으로 로드할지 컴파일 시점에는 알 수 없다.
RuntimeLoading 클래스가 로드될 때는 args[0]에 해당하는 클래스는 아직 로드되지 않으며, main() 메서드가 실행되는 도중 Class.forName(args[0])가 호출되는 순간에 해당 클래스가 로드·링크·초기화된다.
이처럼 컴파일 시점이 아니라 실행 시점에 로드 대상 클래스가 결정되고 로드되는 방식을 런타임 동적 로딩이라고 한다.
장점
단점
https://velog.io/@ddangle/Java-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A1%9C%EB%8D%94%EB%9E%80