본문 바로가기
JVM

Class Loader

by samryong 2024. 3. 13.

 

지난 글에서는 JVM에서 코드가 실행되는 흐름에 대해서 간략하게 알아봤습니다. 이 글에서는 그 과정에서 실행하는 바이트 코드를 읽고 메모리에 올리는 일을 하는 Class Loader에 대해서 자세히 알아보도록 하겠습니다.

 

 

Class Loader 구조

 

Class Loader에는 Loading, Linking, Initialization이 있습니다. 각각의 동작 과정과 역할을 살펴보도록 하겠습니다.

 

1. Loading

Java Application은 동적 로딩입니다(Lazy Loading) 이게 무슨 말 이냐면 Java Application이 실행할때 모든 소스코드를 읽어 놓고 사용하는 것이 아니라 특정 기능을 실행 할 때, 실행에 필요한 데이터를 바이트 코드(.class)에서 읽어와서 사용하는 방식으로 동작을 한다는 뜻입니다. Loading은 이 과정속에서 실행되는 기능에 필요한 데이터를 바이트 코드에서 읽어와서 메모리 (Method 영역)에 저장하는 역할을 합니다.

 

(1)Loading의 종류와 특징

   - 종류 

종류는 기본적으로 Bootstrap, Extension(Platform), Application로 이렇게 3개가 구성되어 있지만 유저가 별도로 Class Loader를 만들 수 있습니다. 

 

   - 특징 

첫째. 가시성 - 자식 ClassLoader는 부모가 로드한 클래스를 볼 수 있지만 자식이 로드한 클래스는 부모가 볼 수 없습니다.

둘째. 고유성 - 부모가 로드한 클래스는 자식이 다시 로드하지 않으므로 효율성이 향상됩니다.

셋째. 위임 계층 구조 - Application ClassLoader는 Extension(Platform) ClassLoader 에 작업을 요청하고 Extestion(Platform) ClassLoader는 Bootstrap ClassLoader에 작업을 요청합니다. 그럼 최상위 Bootstrap ClassLoader는 Class 탐색을 진행하고 Class를 찾을 수 없으면 다시 Extension(Platform)으로 요청을 전달합니다. 그러면 Extension(Platform) ClassLoader는 Class탐색을 진행하고 찾을 수 없으면 다시 Application ClassLoader로 요청을 전달하게 됩니다.

 

https://connie.tistory.com/10

위임 계층 구조의 흐름을 그림으로 나타내면 이렇게 나타납니다. 글로만 봤을땐 흐름이 잘 와닿지 않을 거 같아서 그림을 가져왔습니다.

 

(2)Bootstrap,Extension(Platform), Application Class Loader의 역할과 특징

이 세가지 ClassLoader에 공통점은 실행에 필요한 class를 탐색한다는것입니다. 그럼 각각의 Class Loader는 어떻게 다른지를 알아보겠습니다.

- Bootstrap ClassLoader는 JVM에 필요한 기본적인 실행 클래스들을 찾습니다 주로 /jre/lib/rt.jar에 있는 클래스들을 읽어오는데 native기능(C / C++ 기능)들로 이루어져 있습니다. Bootstrap ClassLoader를 호출해서 확인해보면 null로 출력되는데 이 이유가 Java 객체가 아니기에 null로 표현됩니다.

Classloader 호출

 

- Extension(Platform) ClassLoader는 유저가 정의한 Class에서 Java SE platform API들과 JDK 기능들을 찾는 역할을 합니다.

 

 

- Application ClassLoader CLASSPATH 환경 변수에 지정된 디렉토리 .jar파일 또는 -classpath 또는 -cp커맨드 옵션으로 지정된 위치에서 class를 탐색합니다. 주로 유저가 정의한 class를 탐색한다고 생각하시면 이해가 쉬울것 같습니다.  

 

이렇게 각각의 ClassLoader가 class를 탐색을 마치게 되면 각각의 class에대한 run-time constant pool을 생성하게 됩니다. run-time constant pool에 대해서 간략하게 설명하면 JVM 메모리 영역 5개 (Heap, Method, Stack, PC Register, Native Method Stack)중 Method영역중에 일부분으로 Class, Interface, Method, Method Type, dynamically-computed constant, Static constant들에 대한 참조적 정보들을 저장하하고 있는 곳입니다. 즉 ClassLoader가 class를 탐색하면 메모리 Method 영역에 Class, Interface, Method, 변수, FQCN 정보가 참조형으로 저장이 된다고 기억하면 좋겠습니다.  

 

(3) JDK9에서의 변화

 

 

 

-Extension Classloader의 명칭이 Platform Classloader로 변경되었습니다. 단순히 명칭만 변경된것이 아니라 JDK9부터 도입된 JPMS(Java Platform Module System)으로 기존 jre/lib/rt.jar와 tools.jar가 제거 되었고 그 안에 있던 내용들은 모듈 시스템에 맞게 더 효율적으로 바뀌어서 jmods 폴더에 속해졌습니다.

 

- /lib/ext와 java.ext.dirs(이게 뭔데?! 이전 버전까지 라이브러리나 api를 런타임에 사용할 수 있도록 도와주던 '확장 매커니즘'으로 이해하시면 됩니다.)를 더이상 지원하지 않게 되었습니다.

 

- Platform ClassLoader와 Application Classloader에서 더이상 URLClassLoader를 상속받지 않고 BuiltinClassLoader(모듈화된 어플리케이션에서 ClassLoad의 성능을 향상시킬 class)를 상속 받습니다. 

 

2. Link

링크 단계에서는 프로그램의 원활한 실행을 보장하기 위한 여러가지 준비를 하는 장치들이 있습니다. 이 단계에서는 위에서 말했듯이 참조형으로 저장된 Class, Interface, Method등의 각각의 유형들의 검증을 하고 준비하는 작업들을 수행합니다.

 

(1) Verify

Verification은 Class 와 Interface의 포맷 또는 구조가 올바른지 확인합니다. 검토에 실패하면 byte code verifier에 의해 식별되고 verify exception 오류를 던집니다.

 

(2) Prepare

Prepare 단계에서는 메모리 영역에 올라간 변수들과 static 변수에 필요한 메모리가 초기화 되는 작업을 진행합니다 int 변수에는 0, 객체에는 null, boolean 에는 false등을 할당하여 변수에 맞는 메모리를 확보하게 됩니다. 즉 여기에서 말하는 초기화는 실제 값과는 다르게 각 변수에 맞는 메모리 영역을 확보하기 위해 기본값을 할당하는 작업이라고 생각하시면 될 거 같습니다. 

 

(3)Resolve

이 과정에서 class파일이 갖고있는 symbolic reference(현재 객체에서 다른 클래스를 참조하고있는 것)를 로딩과정에서 Method 메모리에 올려놓은 데이터와 연결시키는 작업을 진행하게 됩니다. 이 단계에서 영향을 받는 JVM Instruction 요소는 new 및 instanceof가 있습니다.

 

3. Initialization

이 단계에서는 이전 단계에서 메모리만 할당한 static, 인스턴스 변수에 실제 값을 할당하게 됩니다. 이단계는 멀티쓰레드 작업이 가능하므로 클래스나 인터페이스의 초기화가 동시에 일어나는 것을 방지하고 쓰레드의 안정성을 가져가야합니다.

 

 

      참고 자료

'JVM' 카테고리의 다른 글

실행 엔진  (0) 2024.04.23
JVM 메모리 구조  (0) 2024.03.13
JVM 동작의 흐름  (0) 2024.03.08