프로그래밍 언어/Java

[Java] ClassLoader

highright96 2021. 11. 2.

클래스로더 역할

클래스로더는 런타임 중에 JVM (Java Virtual Machine)의 Method 영역에 동적으로 Java 클래스를 로드하는 역할을 합니다.

 

클래스로더의 종류

https://engkimbs.tistory.com/606

 

부트스트랩 클래스로더 (Bootstrap classloader)

JVM 시작 시 가장 최초로 실행되는 클래스로더입니다. 부트스트랩 클래스로더는 자바 클래스를 로드하는 것이 아닌, 자바 클래스를 로드할 수 있는 자바 자체의 클래스로더와 최소한의 자바 클래스(java.lang.Object, Class, Classloader)만을 로드합니다.

 

Java 8
jre/lib/rt.jar로 모든 JDK의 내부 클래스를 로드합니다.

Java 9 이후
더 이상 /rt.jar이 존재하지 않으며 /lib 내에 모듈화 되어 포함됐습니다. 이제는 정확하게 ClassLoader 내 최상위 클래스들만 로드합니다.

 

확장 클래스로더 (Extension classloader)

확장 클래스로더는 부트스트랩 클래스로더를 부모로 갖는 클래스로더로써, 확장 자바 클래스들을 로드합니다.  java.ext.dirs 환경 변수에 설정된 디렉터리의 클래스 파일을 로드하고, 이 값이 설정되어 있지 않은 경우${JAVA_HOME}/jre/lib/ext에 있는 클래스 파일을 로드합니다.

 

Java 8
URLClassLoader를 상속하며,  jre/lib/ext 내 모든 클래스를 로드합니다

Java 9
Platfrom Loader로 변경되었으며, URLClassLoader가 아닌 BuiltinClassLoader를 상속합니다.
Inner Static 클래스로 구현되어 있습니다.

 

시스템 클래스로더 (System classloader)

자바 프로그램 실행 시 지정한 Classpath에 있는 클래스 파일 혹은 Jar에 속한 클래스들을 로드합니다. 쉽게 말하자면, 저희가 만든 .class 확장자 파일을 로딩합니다.

클래스 로더가 지켜야 할 세 가지 원칙

위임 법칙 (Delegation Principle)

 

https://engkimbs.tistory.com/606

 

위임 법칙(Delegation Principle)은 클래스 로딩이 필요한 경우 부모 클래스로더 방향으로 클래스 로딩을 위임하는 것입니다.

새로운 클래스를 로드해야 할 때, 다음과 같은 방식으로 로드를 수행합니다.

1) JVM의 Method Area에 클래스가 로드되어 있는지 확인한다. 만일 로드되어 있는 경우 해당 클래스를 사용합니다.
2) Method Area에 클래스가 로드되어 있지 않을 경우, 시스템 클래스로더에 클래스 로드를 요청한다.
3) 시스템 클래스로더는 확장 클래스로더에 요청을 위임합니다.
4) 확장 클래스로더는 부트스트랩 클래스로더에 요청을 위임합니다.
5) 부트스트랩 클래스로더는 부트스트랩 Classpath(JDK/JRE/LIB)에 해당 클래스가 있는지 확인합니다. 클래스가 존재하지 않는 경우 확장 클래스로더에게 요청을 넘깁니다.
6) 확장 클래스로더는 확장 Classpath(JDK/JRE/LIB/EXT)에 해당 클래스가 있는지 확인합니다. 클래스가 존재하지 않는 경우 시스템 클래스로더에게 요청을 넘깁니다.
7) 시스템 애플리케이션 클래스로더는 시스템 Classpath에 해당 클래스가 있는지 확인합니다. 클래스가 존재하지 않는 경우 ClassNotFoundException을 발생시킵니다.

 

가시범위 원칙 (Visibility Principle)

가시범위 원칙은 하위 클래스로더는 상위 클래스로더가 로드한 클래스를 볼 수 있지만, 반대로 상위 클래스로더는 하위 클래스로더가 로드한 클래스를 알 수 없다는 것입니다. 때문에 java.lang.Object 클래스 등 상위 클래스로더에서 로드한 클래스도 하위 클래스로더인 애플리케이션 클래스로더 등에서 사용할 수 있는 것입니다. 이러한 원칙이 없는 경우 클래스로더 간의 상/하위 개념이 사라지기 때문에 이 원칙은 반드시 지켜져야 합니다.

 

유일성의 원칙 (Uniqueness Principle)

유일성의 원칙은 하위 클래스로더가 상위 클래스로더에서 로드한 클래스를 다시 로드하지 않아야 한다는 원칙입니다. 이 원칙이 지켜지지 않는다면 JVM에 동일한 클래스가 2개 이상 로드되는 상황이 발생할 수 있습니다.

 

동적 클래스 로딩 (Dynamic Loading)

자바의 클래스 로딩은 클래스 참조 시점에 JVM에 코드가 링크되고 실제 런타임 시점에 로딩되는 동적 로딩을 거칩니다. 런타임에 동적으로 클래스를 로딩한다는 건 JVM이 클래스에 대한 정보를 갖고 있지 않다는 것을 의미합니다.

 

로드타임 동적 로딩 (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입니다. 두 클래스는 HelloWorld 클래스를 읽어오는 과정에서 로딩됩니다. 이처럼, 하나의 클래스를 로딩하는 과정에서 동적으로 클래스를 로딩하는 것을 로드타임 동적 로딩이라고 합니다.

 

런타임 동적 로딩 (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]에 해당하는 클래스를 로딩합니다. 이처럼 클래스를 로딩할 때가 아닌 코드를 실행하는 순간에 클래스를 로딩하는 것을 런타임 동적 로딩이라고 합니다.

 

참고

'프로그래밍 언어 > Java' 카테고리의 다른 글

[Java] Reflection  (0) 2021.11.21
[Java] PermGen과 Metaspace  (0) 2021.11.03
[Java] Garbage Collection  (0) 2021.10.31
[Java] JVM  (0) 2021.10.25
자바(Java) 버전별 특징  (0) 2021.10.19

댓글