[Java] 클래스 로더란?

땡글이·2023년 3월 31일
9

Java

목록 보기
2/6

앞선 글에서 JVM에 대해 살펴보고, 각각의 구성요소들에 대해선 간단히 알아보았습니다. 이제 자바 바이트코드를 전달받아 동작하는 클래스 로더에 대해 알아보겠습니다.

클래스 로더의 개념

자바는 동적 로드, 즉 컴파일 타임이 아니라 런타임(바이트 코드를 실행할 때)에 클래스를 링크하고 로드하는 특징이 있습니다. 자바의 클래스 로딩은 클래스 참조 시점에 JVM에 코드가 링크되고, 실제 런타임 시점에 로딩되는 동적 로딩을 거칩니다.

클래스를 로딩한다? 링크한다?
일반적으로 클래스를 로딩한다는 것은 런타임 데이터 영역에 클래스를 저장하는 것을 말하고, 클래스를 링크한다는 것은 로딩하는 과정을 포함해 로딩된 클래스 파일에 대해 검증, 준비, 해석의 세 단계를 거치는 것을 의미합니다.

  • 로딩 : 클래스 정보를 런타임 데이터 영역에 저장
  • 링크 : 로딩 + 검증/준비/해석

런타임에 동적으로 클래스를 로딩한다는 것은 JVM이 미리 모든 클래스에 대한 정보를 메소드 영역에 로딩하지 않는다는 것을 의미합니다.

JVM 내에서 자바 바이트코드를 전달받는 클래스 로더는 자바가 동적으로 로드될 수 있도록 해주는 소프트웨어입니다. 정리하자면, 클래스 로더 는 런타임 중에 JVM의 메소드 영역에 동적으로 Java 클래스를 로드하는 역할을 합니다.

근데, 위의 그림을 보면 알 수 있듯이 클래스 로더 내에서 Loading, Linking, Initialization 과정이 있습니다. 각각의 과정이 의미하는 바는 다음과 같습니다.

  • 로딩(Loading)
    • 자바 바이트 코드(.class)를 메소드 영역에 저장한다.
    • 각 자바 바이트 코드(.class)는 JVM에 의해 메소드 영역에 다음 정보들을 저장한다.
      • 로드된 클래스를 비롯한 그의 부모 클래스의 정보
      • 클래스 파일과 Class, Interface, Enum의 관련 여부
      • 변수나 메소드 등의 정보
  • 링크(Linking)
    • 검증(verify): 읽어 들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사한다.
    • 준비(perpare): 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드, 메소드, 인터페이스를 나타내는 데이터 구조를 준비한다.
    • 분석(resolve): 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체한다.
  • 초기화(Initialization)
    • 클래스 변수들(static 변수)을 적절한 값으로 초기화한다. 즉, static 필드들이 설정된 값으로 초기화된다.

클래스로더의 종류

클래스로더에도 종류가 3가지로 나뉜다. 3가지 각각에 대해 살펴본다.

  • 부트스트랩 클래스로더
  • 확장 클래스로더 (혹은 Platform ClassLoader)
  • 어플리케이션 클래스로더 (혹은 System ClassLoader)

부트스트랩 클래스 로더(Bootstrap Class Loader)

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

  • Java 8
    • jre/lib/rt.jar 및 기타 핵심 라이브러리와 같은 JDK의 내부 클래스를 로드한다.
  • Java 9 이후
    • 더 이상 /re.jar이 존재하지 않으며, /lib 내에 모듈화되어 포함됐다. 이제는 정확하게 ClassLoader 내 최상위 클래스들만 로드한다.

확장 클래스 로더 (Extension Class Loader)

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

  • Java 8
    • URLClassLoader를 상속하며, jre/lib/ext 내 모든 클래스를 로드한다.
  • Java 9 이후
    • Platform Loader로 변경되었으며, URLClassLoader가 아닌 BuiltinClassLoader 를 상속한다. Inner Static 클래스로 구현되어 있다.

Java 11 기준으로, 확장 클래스 로더(Platform Class Loader)가 로딩하는 클래스들은 해당 페이지에 있는 클래스들과 같습니다.

애플리케이션 클래스 로더(Application Class Loader)

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

시스템 클래스 로더 (System Class Loader) 라고 불리기도 합니다.

클래스로더의 동작 방식

JVM의 클래스 로더는 새로운 클래스를 로드해야할 때, 다음과 같이 동작하게 됩니다.

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

클래스로더 테스트

public static void printClassLoaders() throws ClassNotFoundException {

    System.out.println("Classloader of this class:"
            + CustomClass.class.getClassLoader());

    System.out.println("Classloader of DriverManager:"
            + DriverManager.class.getClassLoader());

    System.out.println("Classloader of ArrayList:"
            + ArrayList.class.getClassLoader());
}
Classloader of this class:jdk.internal.loader.ClassLoaders$AppClassLoader@7c53a9eb
Classloader of DriverManager:jdk.internal.loader.ClassLoaders$PlatformClassLoader@3498ed
Classloader of ArrayList:null

결과를 보면, CustomClass어플리케이션 클래스로더에 의해 로딩되었고, java.sql.DriverManager확장 클래스로더에 의해 로딩되었고, java.util.ArrayList부트스트랩 클래스로더에 의해 로딩되었다. 부트스트랩 클래스로더에 의해 로딩된 클래스로더는 null로 나온다.

예측한대로 직접 만든 클래스는 어플리케이션 클래스로더에 의해 로딩되는 것을 확인할 수 있었습니다. 또한 기본적인 클래스(java.lang. , java.util. )는 부트스트랩 클래스로더에 의해 로딩되었고, 확장 기능을 위한 java.net. , java.sql.확장클래스 로더(Platform ClassLoader)에 의해 로딩된 것을 확인할 수 있었습니다.


동적 로딩

자바의 클래스 로딩은 클래스 참조 시점에 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을 로딩한다.

이처럼 하나의 클래스를 로딩하는 과정에서 동적으로 다른 클래스를 로딩하는 것을 로드 타임 동적 로딩이라고 한다.

런타임 동적 로딩(Runtime 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] 에 해당하는 클래스를 로딩한다.

이처럼 클래스를 로딩할 때가 아닌, 코드를 실행하는 순간에 클래스를 로딩하는 것런타임 동적 로딩이라고 한다.

동적 로딩의 장점과 단점

  • 장점
    • 모든 클래스를 로드하지 않음으로써, JVM에서는 메모리를 효율적으로 관리할 수 있게 됩니다.
  • 단점
    • 개발자에게 가장 좋은 오류는 컴파일 오류라고 들어볼 수 있다. 동적 로딩은 컴파일 시점에는 오류가 있는지 알 수 없다는 문제점이 있습니다.
    • 또한, 모든 클래스가 로딩되어 있지 않기 때문에, 동적 로딩에 시간이 추가적으로 필요하기 때문에 프로그램 성능 저하로 이어질 수 있습니다.

Reference

https://javacan.tistory.com/entry/1
https://steady-coding.tistory.com/593
https://velog.io/@skyepodium/%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-%EC%96%B8%EC%A0%9C-%EB%A1%9C%EB%94%A9%EB%90%98%EA%B3%A0-%EC%B4%88%EA%B8%B0%ED%99%94%EB%90%98%EB%8A%94%EA%B0%80

profile
꾸벅 🙇‍♂️ 매일매일 한발씩 나아가자잇!

0개의 댓글