(4) Class Loader

Chaeyun·2024년 1월 26일
0

Java

목록 보기
4/6

이전 글인 (3) JVM을 읽고 오면 좋습니다.

Class Loader Subsytem의 역할은?

클래스가 요청될 때 실행할 클래스 파일(.class 바이너리 파일)을 읽고 JVM 메모리에 올려놓는 작업을 처리한다.

세부적으로 로딩 - 링크 - 초기화 세단계로 나뉜다.

  • 로딩(Loading)
    클래스 파일을 바이트 코드로 읽어 메모리로 가져오는 과정이다.
  • 링크(Linking)
    링크는 다시 세 단계로 이루어진다.
    • 검증(Verifying)
      읽어온 바이트 코드가 자바 규칙을 따르는지 확인한다.
    • 준비(Preparing)
      클래스에 정의된 필드, 메소드, 인터페이스들을 나타내는 데이터 구조를 확인하고 필요한 메모리 공간을 확보한다.
    • 로딩(Resolving)
      그 클래스가 참조하는 다른 클래스를 로딩한다.
  • 초기화(Initializing)
    슈퍼 클래스 및 정적 필드들을 초기화한다.

그럼 모든 클래스는 실행되기 전에 메모리에 올라와 있는가?

아니다. 자바는 실행에 필요한 모든 내용을 한 번에 메모리에 로딩하는 게 아니라 실제 클래스가 요청될 때 메모리에 로드한다.

public class Hello {
	public static void main(String[] args){
    	System.out.println("Hello, world");
    }
}

위와 같은 hello.java 파일이 있고 해당 파일을 컴파일 후 실행한다고 가정해보자.
그럼 Hello 클래스를 로드할 때 이 클래스가 참조하고 있는 Object, String, System 클래스가 아직 로드되지 않았으므로 Hello 클래스를 로드하는 일을 중단하고 이 클래스들을 로딩한다(Resolving).

이와 같이 한 클래스의 로드 타임에 필요한 다른 클래스들을 동적으로 로딩하는 것을 로드타임 동적 로딩이라고 한다.

또다른 예시를 보자.

// package com.example.test;

// loadingInterface.java
public interface LoadingInterface {
	public void run();
}

// loadingClass1.java
public class LoadingClass1 implements LoadingInterface {
	@Override
    public void run() {
    	System.out.println("Loading1");
    }
}

// loadingClass2.java
public class LoadingClass2 implements LoadingInterface {
	@Override
    public void run() {
    	System.out.println("Loading2");
    }
}

위와 같이 정의된 인터페이스와 클래스가 있다.

// loading.java
public class RuntimeLoading {
	public static void main(String[] args) {
    	try {
        	Class<?> cls = Class.forName(args[0]);
            Object obj = cls.newInstance();
            LoadingInterface loading = (LoadingInterface) obj;
            loading.run();
        } catch(Throwable e) {
        	e.printStackTrace();
        }
    }
}

위의 RuntimeLoading 파일을 실행하는 경우 아래와 같은 결과가 출력된다.

// 매개변수로 'com.example.text.LoadingClass1'을 넣은 경우
Loading1
// 매개변수로 'com.example.text.LoadingClass2'을 넣은 경우
Loading2

이 경우 LoadingClass1, LoadingClass2 클래스는 RuntimeLoading 파일을 실행하기 전에 미리 메모리에 올라와있지는 않으나, Class.forName() 메소드에 의해 런타임에 로드된다.
이렇게 컴파일 시에는 전혀 알지 못하는 클래스를 런타임에 로드하여 실행하는 경우를 런타임 동적 로딩이라고 한다.

클래스 로더의 종류는?


(이미지 출처 : https://www.cs.rit.edu/~ark/lectures/cl/05_01_00.html)

클래스 로더는 계층 구조로 이루어져 있다.
위에서부터 차례대로 Bootstrap, Extension, System 클래스 로더가 있으며 그 아래에 System 클래스 로더를 확장해 만드는 User-Defined Class Loader가 있다.

  • Bootstrap Class Loader
    • 가장 최상위 클래스로더로, 부모 클래스로더가 없다
    • JRE가 구동하기 위해 필요한 핵심 클래스들을 로드하며, java가 아닌 기계어로 이루어져 있다.
    • ${JAVA_HOME}/jre/lib/rt.jar에 담긴 JDK 클래스를 로드한다. (일반적으로 java.lang.*, java.util.* 등 java로 시작하는 클래스가 포함됨)
  • Extension Class Loader
    • Bootstrap Class Loader를 부모 클래스 로더로 가진다.
    • 자바 extension library를 로드하기 위한 클래스 로더로, ${JAVA_HOME}/jre/lib/ext 폴더나 java.ext.dirs 환경 변수로 지정된 경로에 있는 클래스 파일을 로드한다. (Filesystem utility, Dns library 등이 로드됨)
  • System(=Application) Class Loader
    • Extension Class Loader를 부모 클래스 로더로 가진다.
    • 자바의 classpath 환경 변수 또는 -classpath(또는 -cp) 옵션 값으로 준 경로에 존재하는 클래스 파일들을 로드한다.

클래스 로더의 동작 방식은?

클래스 로더는 위임 모델(Delegation Model)에 따라 동작한다.
1. 클래스 로딩을 요청받은 클래스 로더는 이전에 클래스를 로딩한 적이 있는지 확인한다.
2. 만약 로딩한 적이 없으면 상위 부모 클래스 로더에게 클래스 로딩 요청을 위임한다.
3. 클래스 로딩을 위임받은 부모 클래스 로더 또한 캐시를 먼저 확인하고 이전에 로딩한 적이 없다면 그 클래스 로더의 부모에게 클래스 로딩을 위임한다.
4. 최상위 부트스트랩 클래스 로더까지 요청이 위임되고 이전에 클래스라 로딩된 적이 없다면 최상위 부모부터 자식 클래스 로더 순서로 클래스 로딩을 시도한다.
5. 만약 클래스 로딩에 실패하면 ClassNotFoundException을 발생한다.

References

0개의 댓글

관련 채용 정보