어제 스터디를 하는데 같이 하시는 분이 질문을 주셨다. static 클래스에 관한 얘기였는데, 질문은 완전 다른 방향으로 흘러가서 당황했다.
우선 아래 문제를 풀어보자. 출력 결과를 예상할 수 있겠는가?
class Main {
public static void main(Strin[] args) {
System.out.println(StaticTest.class);
}
}
class StaticTest {
public static String value = “init me!”;
static {
System.out.println(“init static!”);
}
}
출력 결과는
아무 것도 없다.
(질문자와 나는 "init static!"이 출력 결과일 것이라고 생각하고 있었다. 지금 보면 너무 당연하지만, 당시에는 무언가 잘못 생각하고 있었던 것 같다.)
질문은 이거였다.
“클래스가 로드 될 때, 클래스의 스태틱 관련된 정보들이 JVM메모리의 메소드(스태틱) 영역에 올라간다고 했는데 왜 아래 코드가 실행이 되지 않는걸까요?”
우리는 클래스가 로드 될 때, 스태틱 변수들에 값이 할당 되어 메소드 영역에 올라간다고 생각하고 있었다.
이 생각은 잘못된 생각이었다.
이유를 말하자면 바로,
로드(load)와 초기화(initialize)는 별개의 작업이기 때문이다.
자바 공식문서를 보면 Loading, Linking, and Initializing 로 정확하게 구분되어있다.
그리고 초기화가 되는 시점은 아래와 같다. (se17기준)
1. 아래의 JVM 명령어들이 호출될 때.
- getstatic - static 변수를 호출.
- putstatic - static 변수에 값을 할당.
- invokestatic - static 메서드를 호출.
- new - 객체를 생성.
2. 클래스 라이브러리(Class 클래스나 java.lang.reflect)의 특정 메소드를 호출했을 때.
3. 하위 클래스가 초기화 되기 전(클래스인 경우)
4. 구현체가 초기화 되기 전(인터페이스인 경우)
5. JVM이 시작될 때 초기화되도록 설계된 initial class나 interface들.
주의. 상수를 호출할 때는 static이긴 하더라도 초기화가 되지 않는다. (상수는 컴파일시점에 초기화가 된다. 상수의 호출은 컴파일시점에 복사해 놓은 값을 호출하는 것이므로 초기화를 유발하지 않는다.)
위 문제를 다시 보면, StaticTest.class를 호출했을 때, 클래스로더에 의하여 StaticTest클래스가 로드(load) 되는 것은 맞지만, StaticTest클래스가 초기화되는 것은 아니다.
따라서 static 블럭 또한 호출될 일이 없고, 그렇기에 "init static!"이라는 메시지가 출력되지 않은 것이다.
어찌되었던 같이 하시는 분의 질문을 통해서 클래스가 로드될 때, static 변수들이 메모리에 올라간다는 잘못된 생각을 바로 잡을 수 있었다. 좋은 시간이었다.
https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-5.html#jvms-5.5
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.5
https://docs.oracle.com/javase/specs/jvms/se6/html/Concepts.doc.html#19075
https://www.javacodegeeks.com/2018/04/jvm-architecture-jvm-class-loader-and-runtime-data-areas.html
https://docs.oracle.com/javase/specs/jls/se17/html/jls-12.html#jls-12.4
https://www.javacodegeeks.com/2018/04/jvm-architecture-jvm-class-loader-and-runtime-data-areas.html
'java' 카테고리의 다른 글
자바 static 초기화 순서 (0) | 2022.08.29 |
---|