본문 바로가기

Java

JVM 내부 구조 (1) - 클래스 로더

Java 프로그램 전체 동작 과정

 

클래스 로더


JVM 내부의 클래스 로더 상세

  • 자바 소스파일을 컴파일하면 바이트 코드로 이루어진 클래스 파일로 변환
  • 자바 프로그램이 실행되면 클래스 로더는 클래스 파일을 JVM 내부 메모리에 로드함
  • 메모리에 처음 로딩되는 클래스는 일반적으로 main() 메소드가 포함된 클래스
  • 클래스 로더는 크게 세 단계로 처리됨 : 로딩(Loading), 링킹(Linking), 초기화(Initialization)

 

로딩


  • 클래스 파일의 데이터를 생성하고, JVM 내부 메모리 영역에 로드
  • JVM은 ClassLoader.loadClass() 메소드를 통해서 클래스 파일을 메모리에 로드
  • 클래스 파일은 기본 제공 클래스인지, 개발자 정의 클래스인지와 같은 기준에 의해 세 가지 수준으로 나뉨

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

  • 루트 클래스 로더로, 다른 모든 클래스 로더의 부모가 되는 클래스 로더
  • JAVA_HOME/jre/lib 내부의 핵심 자바 패키지 클래스를 로드 (java.lang, java.net, java.util, java.io ...)
  • C, C++ 등의 native 언어로 구현됨

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

  • 부트스트랩 클래스 로더의 자식클래스이고, 애플리케이션 클래스 로더의 부모 클래스
  • 부트스트랩 클래스 로더에 포함되지 않는 핵심 자바 패키지 클래스를 로드 (JAVA_HOME/jre/lib/ext 내부의 localedata, zipfs ..)

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

  • 확장 클래스 로더의 자식 클래스
  • 클래스 패스의 클래스들을 로드
  • 일반적으로 개발자들이 자바 코드로 작성한 클래스 파일들이 로드
public class Test {
	public static void main(String[] args)
	{
    		// String 클래스는 부트스트랩 클래스 로더에 의해 로드되며
        	// 부트스트랩 클래스 로더 POJO가 아닌 native로 구현됐기 때문에 null이 출력
        	System.out.println(String.class.getClassLoader());

         	// Test 클래스는 개발자가 직접 구현한 코드로
        	// 애플리케이션 부트스트랩 클래스 로더에 의해 로드됨
		System.out.println(Test.class.getClassLoader());
	}
}

 

null
jdk.internal.loader.ClassLoaders$AppClassLoader@8bcc55f

 

링킹


  • 클래스 파일이 메모리에 로드된 이후의 프로세스
  • 로드된 클래스 파일들의 의존된 요소들을 연결
  • 링킹은 세 가지 단계로 처리 - 검증(Verification), 준비(Preparation), 해결(Resolution)

검증(Verification)

  • 클래스 파일이 구조적으로 올바른 형식인지, 유효한 컴파일러에 의해 생성되었는지 여부
  • 클래스 파일이 JVM의 조건대로 구현되지 않았을 경우 런타임 에러 java.lang.VerifyError 발생
  • 예를 들어, Java 11로 빌드된 코드가 Java 8 환경에서 실행될 경우 해당 단계에서 에러

준비(Preparation)

  • JVM은 클래스의 static field에 메모리를 할당하고, 데이터를 기본값으로 초기화
private static final boolean ENABLED = true; 

예를 들어, 클래스 내부에 위 코드가 작성되어 있다면 이 단계에서 JVM은 ENABLED 에 boolean 타입의 기본값 false를 입력하며, 이후 Initialization 과정에서 코드에 작성된 true로 변경된다.

해결(Resolution)

  • Symbolic Reference가 런타임 상수 풀에 있던 Direct Reference로 대체됨
Symbolic Reference

자바에서는 특정 객체를 참조할 때 Memory Address를 직접 참조하는 게 아니라 객체의 이름으로 참조하며, 이렇게 객체의 이름으로 참조하는 것을 Symbolic Reference라고 한다.

Symbolic Reference는 Constant Pool에 저장되며 객체에 접근할 필요가 있으면 Constant Pool에서 Symbolic Reference를 통해 해당 객체의 Memory Address를 찾아 동적으로 연결한다.

 

초기화


  • 클래스의 초기화 메소드 또는 생성자를 호출
  • static block 실행 및 static field 값 할당
private static final boolean ENABLED = true;  # true 값이 실제 할당됨

 

JVM은 멀티 스레드 기반이기 때문에, Initialization은 멀티 스레드로 동작하고, 동시성 이슈를 유발할 수 있다. 따라서 멀티 스레드 환경에서 프로그램이 정상적으로 동작할 수 있도록 thread safety하게 처리해줘야 한다.

 

 

 

 

https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/

https://www.geeksforgeeks.org/jvm-works-jvm-architecture/?ref=lbp

'Java' 카테고리의 다른 글

JVM 내부 구조 (2) - 런타임 데이터 영역  (1) 2023.11.29
Java 프로그램의 실행 과정  (0) 2023.11.27
JVM, JDK, JRK  (0) 2023.11.27