본문 바로가기
💊 Java/- Java Lang

[조금 더 깊은 Java] JRE의 Classloader 에 대해 알아보자

by Wonit 2021. 12. 2.

 

오늘은 JRE의 핵심 구성요소인 Classloader에 대해서 알아보려 한다.

 

지난 시간 우리는 Java Bytecode 에 대해서 알아보았다.

 

[조금 더 깊은 Java] Java Bytecode 를 알아보자 (자바를 컴파일하면 어떤 일이 일어날까?)

우리는 많은 시간 Java를 이용해서 다양한 소프트웨어를 개발하면서 들었던 소리가 있다. Java는 JVM 이 있기 때문에 플랫폼에 종속적이지 않고 이식성이 뛰어나다. 그 이유에 대해서 생각해본 경

wonit.tistory.com

 

우리가 .java 파일을 컴파일한다면 .class 라는 바이트 코드로 변환이되고 JVM이 해당 class 파일을 동적으로 실행하는 과정에서 가장 중요한 것이 바로 ClassLoader 이다.

 

Java ClassLoader

 

Java의 ClassLoader 는 JRE의 구성요소 중 하나이다.

 

JRE는 JVM과 Core Java Library 를 포함하며 ClassLoader 도 함께 포함한다.

 

javac 컴파일러에 의해 사용자가 작성한 .java 소스코드를 컴파일하여 Bytecode 를 생성하고 해당 .class 파일을 Runtime 으로 가져가는 이 시점에 Java ClassLoader 가 동작한다.

 

이 시점에서 다시 한 번 자바 클래스로더의 정의를 내려보자면

 

자바 바이트코드를 JVM 으로 동적 로드하는 Java Runtime Environment (JRE)의 일부이다.

 

이러한 Java ClassLoader 가 있기 때문에 Host PC의 파일과 파일 시스템에 대해서 알 필요가 없고 플랫폼에 독립적이게 되는 것이다.

위에서 동적 로드하는 라는 말을 썼는데 이 말은 무슨 말일까?

 

자바 클래스로더는 .class 파일을 한 번에 모두 읽어 필요한 의존 관계의 .class 파일을 불러오지 않는다.

 

클래스로더가 파일을 읽으며 필요한 시점에 동적으로 로드하고 이는 즉, 클래스가 프로그램에 의해 호출될 때 까지 로드하지 않는다는 것을 의미한다.

 

위와 같은 특성은 몇가지가 더 존재하는데 하나씩 알아보도록 하자.

 

Java ClassLoader 의 특징

 

Java Classloader, 자바 클래스로더는 3가지 대원칙이 존재한다.

 

  1. Delegation
  2. Visibility
  3. Uniqueness

 

특징 1, Deligation

 

클래스로더는 하나만 존재하지 않는다.

 

클래스로더는 일반적으로 여러 개의 클래스로더가 존재하며 각각의 클래스로더는 하는 일이 정해져있다.

 

이러한 클래스로더들 사이의 관심사에 따라 처리를 할지 말지 결정하는 것을 바로 위임 모델 이라고 하며 계층 구조를 바탕으로 동작한다.

 

main() 메서드가 존재하는 ClassLoaderRunner 클래스에서 사용자가 정의한 Internal 클래스를 로드하는 과정을 위임, Delegation에 흐름으로 따라가보자.

 

  • ClassLoaderRunner 클래스는 ApplicationClassLoader 에게 Internal 클래스를 로드하라고 요청한다.
  • ApplicationClassLoader 는 직접 로드하지 않고 부모인 ExtensionClassLoader 에게 Internal 클래스를 로드하라고 요청한다.
  • 역시 ExtensionClassLoader 는 직접 로드하지 않고 부모인 BootstrapClassLoader 에게 Internal 클래스를 로드하라고 요청한다.
  • BootstrapClassLoaderrt.jar 에서 클래스가 존재하는지 확인한다.
  • 있다면 클래스를 로드하고 없다면 ExtensionClassLoader에서 ApplicationClassLoader 까지 앞의 과정을 반복한다.
  • 만약 존재하지 않는다면 ClassNotFoundException 을 throw 한다.

 

특징 2, Visibility

 

Visibility Principle 은 클래스로더의 가시범위는 상위 클래스로더만 확인할 수 있다는 것이다.

 

더 풀어서 이야기하자면

 

하위 클래스로더는 상위 클래스로더가 로딩한 클래스를 확인할 수 있지만 상위 클래스로더는 하위 클래스로더가 로딩한 클래스를 확인할 수 없다.

 

이 Visibility 를 잘 지켜야만 Java ClassLoader 가 되는데, 이 제약이 바로 다음에 나올 Uniqueness 이다.

 

특징 3, Uniqueness

 

유일성, Uniqueness 원칙은 Visibility 의 특성을 이용해서 클래스의 Duplicate loading 을 막을 수 있게 된다.

 

즉, 하위 클래스로더는 상위 클래스로더가 로딩한 클래스를 다시 로딩하지 않는 것을 보장한다.

 

이러한 유일성을 지키기 위해서 Visibility 이외에도 Class Binary name을 이용하는데, 이를 FQCN, Fully Qualified Class Name 이라고 한다.

 

이미 로드된 클래스인지 확인하기 위해서는 Namespace 에 보관된 FQCN을 기준으로 클래스를 찾아보고 없다면 Delegation 을 통해서 클래스를 로드한다.

 

클래스로더의 종류

 

위에서 나온 Delegation 모델에서 ApplicationClassLoaderBootstrapClassLoader 와 같이 잘 알려진 클래스로더들이 존재하는데, 대표적인 JRE의 클래스로더는 3가지가 있다.

 

 

  1. BootstrapClassLoader
  2. ExtensionClassLoader
  3. ApplicationClassLoader

 

이들은 각각 다음과 같은 역할을 수행한다.

 

  • Bootstrap Classloader
    • JVM을 기동할 떄 생성됨.
    • Object 클래스를 비롯한 자바 API 를 로드하는 Classloader
  • Extension Classloader :
    • 기본 자바 API를 제외한 확장 클래스들을 로드하는 Classloader
  • User-Defined Class Loader :
    • 애플리케이션 사용자가 직접 코드 상에서 생성해서 사용하는 Classloader

 

다음 코드를 참고해보자

 

public class Main {
    public static void main(String[] args) {
        System.out.println("우리가 정의한 클래스 : " + Main.class.getClassLoader()); // jdk.internal.loader.ClassLoaders$AppClassLoader
        System.out.println("Logging 클래스 : "
        + Logging.class.getClassLoader()); // Logging:sun.misc.Launcher$ExtClassLoader
        System.out.println("Java Util에 포함된 클래스 " + String.class.getClassLoader()); // null
    }
}

 

위의 코드에서 3가지의 대표적인 클래스로더를 모두 확인할 수 있다.

 

위에서 3번째인 BootstrapClassLoader 는 출력 결과가 Null 로 보이는데, 이 이유는 Native C 로 구현이 되어있기 때문에 Java Runtime 에서 Load를 할 수 없기 때문이다.

 

클래스로더는 3가지 클래스로더 이외에도 우리가 직접 만들어서 사용할 수 있다.

 

ClassLoader가 클래스를 로드하는 동작 과정

 

위에서 본 특징과 원칙들을 만족하는 환경에서 클래스로더는 다음과 같은 과정을 거쳐 클래스를 로드하고 링크하며 초기화를 한다

 

 

  • 로드 Loading
    • Class file 을 가져와서 JVM의 메모리에 로드한다.
  • 검증 Verifying
    • Java Language Specification 및 JVM 명세에 위배된 정보가 있는지 검사한다.
    • 가장 복잡하고 시간이 오래걸리는 부분이다.
  • 준비 Preparing
    • 클래스가 필요로하는 메모리를 할당한다.
    • 클래스에서 정의된 필드, 메서드, 인터페이스를 나타내는 struct 를 준비한다.
  • 분석 Linking
    • 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다.
  • 초기화 Initializing
    • 클래스 변수들을 적절한 값으로 초기화한다.

 

결론

우리는 이번에 클래스로더란 무엇인가, 클래스로더의 특징 및 원칙, 클래스로더의 동작 과정, 클래스로더의 종류에 대해서 알아보았다.

 

이런 클래스로더가 직접 클래스파일을 불러들이기 때문에 JVM은 직접적인 클래스와 연관이 생기지 않고 독립적으로 동작할 수 있게 되는 것이다.

 

댓글0