스프링 프레임워크를 사용하며 @Autowired로 private 필드에 의존성을 주입받는 것을 보면 '마법 같다'는 생각을 하곤 합니다. 이 '마법'의 근간이 되는 기술 중 하나가 바로 자바 리플렉션(Reflection)입니다.
리플렉션은 "런타임에 클래스 정보를 읽고 조작하는 기술" 입니다. 하지만 이 강력한 기술을 제대로 이해하려면, JVM이 .class 파일에 생명을 불어넣는 '클래스 로딩' 과정을 알아야 합니다.
Java는 프로그램을 실행할 때 모든
.class파일을 한꺼번에 메모리에 올리지 않습니다. 대신동적 로딩(Dynamic Loading)방식을 사용합니다.
즉, 런타임(Runtime) 중에 해당 클래스가 '처음으로 필요해지는 순간'에만 메모리에 적재합니다. 이 "처음으로 필요한 순간"을 JVM 명세에서는 '능동적 사용(Active Use)'이라고 부르며, 주요 시점은 다음과 같습니다.
new Student()처럼 객체 인스턴스를 생성할 때.Student.schoolName이나 Student.getSchoolName()처럼 static 필드나 메서드를 사용할 때. (단, 예외 있음)Class.forName("com.example.Student")처럼 클래스 로딩을 강제할 때.🧐 static 멤버 접근 예외가 뭐가 있을까?
public static int a = 10;System.out.println(MyClass.a);a의 값 10은 MyClass의 초기화가 실행되어야만 알 수 있습니다.예외입니다.public static final int b = 20;System.out.println(MyClass.b);b는 final이 붙은 상수입니다. 컴파일러는 MyClass.b를 호출하는 코드(예: Main.class)에 b의 값 20을 그냥 복사해서 넣어버립니다.초기화도 일어나지 않습니다.결론: static 멤버에 접근하더라도, 그것이 static final 상수라면 클래스 '초기화'를 유발하는 '능동적 사용'에서 제외됩니다. 이것이 바로 "단, 예외 있음"의 의미입니다.
능동적 사용이 발생하면, 클래스 로더는.class파일을 찾아 JVM 메모리에 올립니다. 이 과정은 크게 로딩(Loading) → 링킹(Linking) → 초기화(Initialization)의 3단계를 거칩니다.
🗺️ JVM 클래스 로딩과 메모리 구조

🔬 다이어그램 상세 분석
1단계: 로딩 (Loading) - 생성과 즉각적인 연결
메소드 영역 (Metaspace): JVM이 코드를 실행하기 위해 필요한 "설계도 원본(메타데이터)"을 이곳에 저장합니다.
힙 영역 (Heap): 개발자가 리플렉션을 하기 위해 필요한 "Class 객체(관문/리모컨)"를 힙에 단 하나 생성합니다. 이 Class 객체는 Metaspace에 있는 "설계도 원본"을 가리키는 내부 참조를 갖습니다.
2단계: 링킹 (Linking) - 기본값 할당 (즉시 실행)
로딩과연결이 끝난 직후, JVM은링킹을 수행합니다.링킹은 [검증 -> 준비 -> 해석]으로 나뉘며, 핵심은 준비입니다.
설계도 원본 내부의 static 변수 메모리를 할당하고, 기본값(0, null)으로 자동 초기화합니다.3단계: 초기화 (Initialization) - 실제 값 할당 (지연 실행)
초기화프로세스가 바로 '능동적 사용'이 필요한 이유입니다. 이 단계는 게으르게(Lazy) 동작합니다.
능동적 사용이 최초로 발생할 때까지 미뤄집니다.리플렉션의 시작은 Heap에 존재하는 Class 객체(리모컨)를 획득하는 것입니다.
Class 객체를 얻는 3가지 방법
이 Class 객체를 통해 개발자는 '메소드 영역(Metaspace)'에 저장된 '설계도 원본'을 런타임에 조회하고 조작할 수 있게 됩니다.
[참고] Class.forName("...") vs Class.forName("...", false, ...)
Class.forName("...")
- 동작: 클래스의
로딩 → 링킹 → 초기화까지 모두 수행Class.forName("...", false, ...)
- 동작(initialize = false): 클래스의
로딩 → 링킹까지만 수행- 결과: 기본값(0, null) 상태로 남아있으며, static { ... } 블록은 실행X
- 용도: 프레임워크의 클래스 스캔, 메타데이터만 조회할 때