"스프링 입문을 위한 자바 객체 지향의 원리와 이해"라는 책을 읽다가 내가 동적 로딩에 대해서 잘못 알고 있었음을 알게 되었다.
다음과 같은 소스코드가 있다.
public class A {
public static void main(String[] args) {
System.out.println("main");
System.out.println(B.value);
}
}
class B {
static int value = 0;
static {
System.out.println("B");
}
}
위와 같은 코드를 실행하면, 나는 B -> main -> 0이 호출될 것이라고 생각했으나, 정답은 main -> B -> 0 순으로 호출된다. 처음에는 그 이유가 Java의 동적 로딩(Dynamic loading) 때문이라고 생각했다.
그래서 동적 로딩에 대해 내가 잘못 알고 있구나 생각하여 동적 로딩에 대해 공부했다.
이전까지는 동적로딩이란 호출하고 있는 정보들의 FQCN을 보고, 필요한 정보들이 없으면 그때 로딩하는 것으로 알고 있었다. 그런데 이 시점이 실행하는 시점이 아니라, 실행하기 전 미리 올려두는 것으로 생각해 B -> main -> 0 순이라고 생각했고, 이게 잘못된 것이라 생각해서 동적 로딩에 대해서 공부해 보았다.
다음 글을 읽고 보니 동적 로딩에는 다음과 같이 2가지가 존재한다고 한다. 로드타임 동적로딩(load-time dynamic loading)과 런타임 동적 로딩(run-time dynamic loading) 이렇게 2가지가 있다.
내가 이해한 바로는 로드타임 동적 로딩은 위처럼 클래스 정보가 컴파일시 명시적일 때 동작하는 것이고, 런타임 동적 로딩과 같이 추상형을 바라보고 있거나, 리플랙션 등의 이유로 런타임이 아니라면 확인할 수 없는 경우에 적용되는 것으로 이해했다.
실험한 내용
그런데 이상한 점이 있다. 다음과 같은 소스는 분명 명시적인 클래스이기 때문에 가설대로라면, 로드타임 동적로딩일 경우 B -> main -> 0 순으로 호출되어야 한다. 그런데 main -> B -> 0 순으로 출력하고 있다.
따라서 동적 로딩은 이 문제의 원인이 아닌 것으로 판단하고 다른 조사를 해보았다.
오명운님 블로그의 Back to the Essence - Java 컴파일에서 실행까지 - (2) 글을 보면, 다음과 같은 내용이 나온다. 정적 필드를 기본값으로 초기화 하는 것은 링크의 준비 단계에서 수행되고, 정적 필드를 특정값으로 초기화 하는 것은 초기화 단계에서 수행된다. 지금 설명하고 있는 이 "클래스 또는 인터페이스 초기화 메서드"가 실행되는 것이 초기화 단계다.