자바(Java) 프로그램이 컴파일되면 소스 코드는 바이트코드(byte code) 형태로 변환되어 .class 파일에 저장됩니다. 이렇게 저장된 클래스를 실제 실행 시점에 필요할 때마다 메모리에 불러오는 역할을 하는 것이 바로 클래스로더(ClassLoader)입니다.
클래스로더는 계층적(hierarchical) 구조로 동작합니다. 따라서 클래스 로딩 요청이 발생하면 해당 요청은 먼저 부모 클래스로더(parent class loader)에게 위임되며, 부모가 처리하지 못하는 경우에만 자식 클래스로더가 직접 클래스를 로드하게 됩니다. 이러한 방식을 '위임 모델(Delegation Model)'이라고 합니다.
자바 ClassLoader의 종류
자바에는 크게 세 가지 유형의 클래스로더가 존재합니다.
- 부트스트랩 클래스로더(Bootstrap ClassLoader): JVM 최상위 클래스로더로, java.lang.String, java.util.HashMap과 같은 코어 자바 API(rt.jar 등)를 로드합니다. 네이티브 코드로 구현되어 있어 자바 객체가 아닙니다.
- 익스텐션 클래스로더(Extensions ClassLoader): JDK 확장 디렉터리(jre/lib/ext)에 위치한 확장 클래스들을 로드합니다.
- 시스템 클래스로더(System ClassLoader): 애플리케이션 클래스패스(classpath)에 있는 사용자 정의 클래스들을 로드하며, 일반적으로 개발자가 작성한 코드를 처리합니다.
예제 코드
아래 예제는 서로 다른 클래스들이 어떤 클래스로더에 의해 로드되는지 확인하는 코드입니다.
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println("class loader for this class: " + ClassLoaderTest.class.getClassLoader());
System.out.println("class loader for DNSNameService: " + sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
System.out.println("class loader for HashMap: " + java.util.HashMap.class.getClassLoader());
}
}실행 결과
class loader for this class: sun.misc.Launcher$AppClassLoader@73d16e93 class loader for DNSNameService: sun.misc.Launcher$ExtClassLoader@70dea4e class loader for HashMap: null
결과 분석
실행 결과를 살펴보면 각 클래스의 특성에 따라 서로 다른 클래스로더가 동작한 것을 확인할 수 있습니다.
- ClassLoaderTest: 개발자가 작성한 애플리케이션 클래스이므로 애플리케이션 클래스로더(AppClassLoader)가 로드했습니다.
- DNSNameService: JDK 확장 라이브러리에 속한 클래스이므로 익스텐션 클래스로더(ExtClassLoader)가 로드했습니다.
- HashMap: 코어 자바 API 클래스이므로 부트스트랩 클래스로더가 로드했습니다. 부트스트랩 클래스로더는 네이티브(C/C++)로 구현되어 자바 객체가 아니기 때문에 결과값이
null로 출력됩니다.
이처럼 클래스로더의 계층 구조와 위임 모델을 이해하면 자바의 클래스 로딩 메커니즘뿐만 아니라 ClassNotFoundException, NoClassDefFoundError 같은 오류의 원인 분석에도 큰 도움이 됩니다.