클래스로더 너는 누구심까
- 자바는 동적로드, 즉 컴파일 타임이 아니라 런타임에 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 특징이 있다.
- 이 동적로드를 담당하는 부분이 바로 클래스로더의 역할이다.
여기서 먼저 알고 넘어가야할 사항으로 클래스로더는 실행할 때 클래스들을 한번에 로드하지 않는다. 메모리 낭비이기 때문이지요 !
따라서 그때그때 실행을 하면서 필요한 것들만 클래스 로더가 가져오게 된다.
실행엔진이 실행하다가 클래스가 필요한 순간이 오면 클래스 로더에게 요청을 하고 클래스 로더는 클래스를 로드해서 응답해주는 방식이다 !
클래스로더 계층구조 간략히 정리하기

- 부트스트랩 클래스 로더
- 최상위 클래스로더로 유일하게 자바가 아니라 네이티브 코드로 구현이 되어있다.
- JVM이 실행될 때 같이 메모리에 올라간다.
- Object클래스를 비롯하여 JAVA API들을 로드한다.
- 익스텐션 클래스 로더
- 기본 자바 API를 제외한 확장 클래스들을 로드한다.(다양한 보안 확장기능 로드)
- 시스템 클래스 로더
- 부트스트랩과 익스텐션 클래스로더가 JVM 자체의 구성요소들을 로드한다면, 시스템 클래스 로더는 애플리케이션의 클래스들을 로드한다.
- 사용자가 지정한 $CLASSPATH 내의 클래스들을 로드한다.
- 사용자 정의 클래스 로더
- 애플리케이션 사용자가 직접 코드상에서 생성하여 사용하는 클래스 로더
클래스 로드의 과정
- 로딩 : .class 파일을 JVM 메모리에 로드하는 과정이다.
- 링킹 :
- 검증 : 바이트코드가 제대로 자바의 규칙을 따르고 있는지 검증한다 !
- 준비 : 클래스가 필요로 하는 메모리 양을 미리 할당해둔다 !
- 분석 : 클래스가 참조하는 객체에 대해 실제 메모리 주솟값을 대입하게 된다 !
- 초기화 : 클래스 안에 스테틱 변수가 있겠지? 그럼 그 변수의 값을 할당하고 초기화 시키는 단계이다.
클래스 로드 요청 과정
- 캐시에 해당 클래스가 있는지 확인을 한다.
- 캐시란? 미리 불러온 것들을 저장하는 것을 의미하며 즉 이미 불러온 클래스가 있는지 확인한다.
- 상위 클래스 로더에서 확인을 한다. 해당 클래스를 가져온게 있는지 없다면 하위 클래스 없으변 본인 클래스 로더에서 가져오게 된다.
클래스 로드 예제
로드와 초기화는 다르다
- 클래스로드 = loadClass(String)가 클래스를 초기화 하지 않는다.
public class OuterTest { private static final String CALL_1 = "A"; static { System.out.println("B"); } private static class InnerTest { static { System.out.println("C"); } public static String print() { return "D"; } } public static void main(String[] args) { System.out.println(Call_1); System.out.println(InnerTest.class); System.out.println(InnerTest.print()); } }
실행결과

로드과정
- 프로그램을 실행하면 OuterTest 클래스가 초기화된다.
- static CALL_1 필드는 static 블록 전에 초기화된다.
- InnerTest 클래스는 아직 초기화 되지 않았다 !
- Main 메서드가 호출되면서 CALL_1 필드값을 출력한다.
- System.out을 통해 Inner 클래스를 나타냅니다.
- Inner 클래스는 초기화 되지 않았지만 로드는 된 상태이다.(메모리 모델에 참조가 있는 이유이다.)
- System.out은 Inner(Inner.print)의 print 메서드에 엑세스해야 하며, print 메서드의 결과를 반환하기 전에 InnerTest 클래스를 초기화 해야한다. 그렇기 때문에 static이 4단계이다.
- 마지막으로 System.out에는 표시해야 할 모든 데이터가 있고 마지막 줄이 콘솔에 표시된다.
로드와 초기화의 차이??
- Main 메소드를 포함시키고 있는 Outer 클래스는 Main 메소드를 실행하기 전에 초기화가 진행된다. 그렇기 때문에 static 변수와 static 블록이 초기화가 진행되어, 호출되게 된다.
- Inner.class를 호출하는 부분에서 해당 클래스는 메모리에 올라가 있기 때문에 참조값이 나오게 된다. 이는 클래스는 로드가 되어 있는 상태라는 의미를 가지고 있다. 하지만 static 블록이 호출되지 않았기 때문에 해당 Inner 클래스는 아직 초기화 전 이라는 것을 알 수 있다.
- print 메서드를 호출하는 부분에서 print를 호출하려면 Inner클래스가 초기화 되어있어야 한다. 그렇기에 메서드를 호출하기 전 클래스의 초기화가 진행이 되었고 초기화가 진행됨에 따라 static 블록을 실행이 된다.
번외
로드 === 초기화?
- JVM 설명 글을 읽을 때에 Loading > Linking > Initialization 순으로 되는 걸로 보이지만 막상 진행하면 Loading ≠ Initalization 이다.
static이 올라가는 시점?
- 클래스 로더를 보기 전에는 자바가 올라갈 때 모든 static으로 정의된 것들이 한번에 올라가는 줄 알고 있었다.
- 하지만 아무리 static으로 정의 된 값이라 하더라도 모두 JVM이 시작하자마자 올라가는건 너무 비효율 적인 방법이기 때문에 바로 모두 올리질 않는다.
- 따라서 클래스가 시작할 때 초기화가 되면서 올라가는게 맞는 방법이다. 그 이유는 그 클래스에서 클래스를 통해 사용하기 위해 정의 된 static 이기 때문이다.
- 하지만 대부분 사람들은 로딩 or 로드가 이루어진다고 말하며 해당 InnerTest Class를 지연로딩이라 말한다.
static 키워드를 다시 파악해보자
- 사실 모든 클래스는 원래 static 메모리에 올라가는 static들이다. 클래스에 static을 붙이게 되면 인스턴스의 생성 방식이 달라지기만 할 뿐,
- static이 붙지 않은 경우
- new 생성자를 통해 인스턴스 생성 후 만들어진 인스턴스를 통해 접근이 가능해진다.
- static이 붙은 경우
- 내부 클래스의 인스턴스에 바로 접근이 가능해진다.
- 또한 static 메소드 에서는 static 메소드만 호출할 수 있다.
- 그렇기 때문에 정적 팩토리 메서드를 통해 진행되는 싱글톤 패턴을 Inner class로 사용하려면 해당 클래스 또한 static이어야만 호출할 수 있게 된다.
결론
- 클래스는 호출될 때 초기화를 진행한다.
- 우리는 대부분 new 생성자를 통해서 클래스를 초기화 하며
- 이때 static으로 선언된 애들이 올라가게 된다.
- 하지만, static은 유일한 하나이기 때문에 한번 올라가면 새로 해당 클래스를 초기화를 진행하더라도 먼저 올라간 걸 호출해서 사용한다.