클래스 로더란?
자바는 컴파일 타임이 아니라 런타임에 클래스 로드하고 링크하는 동적 로드를 하는 특징이 있다.
이 동적 로드를 담당하는 부분이 JVM의 클래스 로더이다.
클래스 로더는 로딩(Loading), 링킹(Linking), 초기화(Initializing) 단계를 거쳐 JVM에서 사용할 수 있게 된다.
- 로딩(Loading)
- 자바 바이트 코드(.class)를 메소드 영역에 저장한다.
- 각 자바 바이트 코드(.class)는 JVM에 의해 메소드 영역에 다음 정보들을 저장한다.
- 로드된 클래스를 비롯한 그의 부모 클래스의 정보
- 클래스 파일과 Class, Interface, Enum의 관련 여부
- 변수나 메소드 등의 정보
- 링킹(Linking)
- 검증 : 읽어 들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사한다.
- 준비 : 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드, 메소드, 인터페이스를 나타내는 데이터 구조를 준비한다.
- 분석 : 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체한다.
- 초기화(Initializing)
- 클래스 변수들을 적절한 값으로 초기화 한다. 즉, static 필드들이 설정된 값으로 초기화한다.
클래스 로더의 종류
클래스 로더는 계층별로 다음과 같이 구성되어 있다.
- 부트 스트랩 클래스 로더(Bootstrap Class Loader)
- 확장 클래스 로더(Extension Class Loader)
- 시스템 클래스 로더(System Class Loader)
부트스트랩 클래스 로더(Bootstrap Class Loader)
JVM 시작 시 가장 최초로 실행되는 클래스로더이다.
자바 클래스를 로드하는 것이 아닌, 자바 클래스를 로드할 수 있는 자바 자체의 클래스 로더와 최소한의 자바 클래스(java.lang.Object, Class, ClassLoader)만을 로드한다.
Java 8
jre/lib/rt.jar
및 기타 핵심 라이브러리와 같은 JDK의 내부 클래스를 로드한다.
Java 9 이후
더 이상 /re.jar
이 존재하지 않으며, /lib
내에 모듈화되어 포함됐다. 이제는 정확하게 ClassLodaer 내 최상위 클래스들만 로드한다.
확장 클래스 로더(Extension Class Loader)
확장 클래스 로더는 부트스트랩 클래스 로더를 부모로 갖는 클래스 로더로서, 확장 자바 클래스들을 로드한다.
java.ext.dirs
환경 변수에 설정된 디렉토리의 클래스 파일을 로드하고, 이 값이 설정되어 있지 않은 경우 ${JAVA_HOME}/jre/lib/ext
에 있는 클래스 파일을 로드한다.
Java 8
URLClassLoader를 상속하며, jre/lib/ext
내 모든 클래스를 로드한다.
Java 9 이후
Platform Loader로 변경되었으며, URLClassLoader가 아닌 BuilitinClassLoader를 상속한다. Inner Static 클래스로 구현되어 있다.
시스템 클래스 로더(System Class Loader)
자바 프래그램 실행 시 지정한 Classpath에 있는 클래스 파일 혹은 jar에 속한 클래스들을 로드한다.
쉽게 말하면, 우리가 만든 .class 확장자 파일을 로드한다.
클래스 로더의 동작 방식
- JVM의 메소드 영역에 클래스가 로드되어 있는지 확인한다. 만일 로드되어 있는 경우 해당 클래스를 사용한다.
- 메소드 영역에 클래스가 로드되어 있지 않을 경우, 시스템 클래스 로더에 클래스 로드를 요청한다.
- 시스템 클래스 로더는 확장 클래스 로더에 요청을 위임한다.
- 확장 클래스 로더는 부트스트랩 클래스 로더에 요청을 위임한다.
- 부트스트랩 클래스 로더는 부트스트랩 Classpath(JDK/JRE/LIB)에 해당 클래스가 있는지 확인한다. 클래스가 존재하지 않은 경우 확장 클래스 로더에게 요청을 넘긴다.
- 확장 클래스 로더는 확장 Classpath(JDK/JRE/LIB/EXT)에 해당 클래스가 있는지 확인한다. 클래스가 존재하지 않을 경우 시스템 클래스 로더에게 요청을 넘긴다.
- 시스템 클래스 로더는 시스템 Classpath에 해당 클래스가 있는지 확인한다. 클래스가 존재하지 않는 경우 ClassNotFoundException을 발생시킨다.
클래스 로더가 지켜야 할 세 가지 원칙
- 위임 원칙
- 클래스 로더는 클래스 또는 리소스를 찾기 위해 요청을 받았을 때, 상위 클래스 로더에게 책임을 위임하는 위임 모델을 따른다.
- 가시 범위 원칙
- 하위 클래스 로더는 상위 클래스 로더가 로드한 클래스를 볼 수 있지만, 반대로 상위 클래스 로더는 하위 클래스 로더가 로드한 클래스를 알 수 없다.
- 이로 인해
Java.lang.Object
클래스 등 상위 클래스 로더에서 로드한 클래스도 하위 클래스 로더인 시스템 클래스 로더 등에서 사용할 수 있다.
- 유일성의 원칙
- 하위 클래스 로더가 상위 클래스 로더에게 로드한 클래스를 다시 로드하지 않아야 한다는 워닟ㄱ이다.
- 위임 원칙에 의해서 위쪽으로 책임을 위임하기 때문에 고유한 클래스를 보장할 수 있다.
동적 클래스 로딩
동적 클래스 로딩에는 로딩하는 시점에 따라 두 가지 방식이 있다.
- 로드 타임 동적 로딩(Load-time Dynamic Loading)
- 런타임 동적 로딩(Run-time Dynamic Loading)
로드타임 동적 로딩(Load-time Dynamic Loading)
public class HelloWorld {
public static void main(String[] args) {
System.out.println("안녕하세요!");
}
}
- JVM이 시작되고 부트스트랩 클래스 로더가 생성된 후에 모든 클래스가 상속받고 있는 Object 클래스를 읽어온다.
- 클래스 로더는 명령 행에서 지정한 HelloWorld 클래스를 로딩하기 위해, HelloWorld.class 파일을 읽는다.
- HelloWorld 클래스를 로딩하는 과정에서 필요한 클래스인
java.lang.String
과java.lang.System
을 로딩한다.
이처럼 하나의 클래스를 로딩하는 과정에서 동적으로 다른 클래스를 로딩하는 것을 로드 타임 동적 로딩이라고 한다.
런타임 동적 로딩(Run-time Dynamic Loading)
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.forName()
메소드가 실행되기 전까지는 RuntimeLoading 클래스에서 어떤 클래스를 참조하는지 알 수 없다.- 따라서 RuntimeLoading 클래스를 로딩할 때는 어떤 클래스도 읽어오지 않고, RuntimeLoading 클래스의
main()
메소드가 실행되고Class.forName(args[0])
를 호출하는 순간에 비로소args[0]
에 해당하는 클래스를 로딩한다.
이 처럼 코드를 실행하는 순간에 클래스를 로딩하는 것을 런타임 동적 로딩이라고 한다.
참고 자료 :
https://steady-coding.tistory.com/593
https://velog.io/@yeoro/JVM-Java-Virtual-Machine
'백엔드 > Java' 카테고리의 다른 글
[Java] 리플렉션(Reflection) (0) | 2023.05.25 |
---|---|
[Java] 프록시 패턴(Proxy Pattern) (0) | 2023.05.24 |
[Java] 상속(Inheritance)과 합성(Composition) (1) | 2023.05.19 |
[Java] 옵저버 패턴(Observer Pattern) (1) | 2023.05.17 |
[Java] 전략 패턴(Strategy Pattern) (0) | 2023.05.17 |