Non-static field '인스턴스' cannot be referenced from a static contex
static과 관련해서 위와같은 오류 메시지를 흔하게 만날 수 있다. 왜 이러한 오류 문구가 나타나는지, static이란 어떤 녀석인지 자세히 알아보자!
public class MyClass {
public static int classVariable; // 클래스 변수 또는 static 변수
public int instanceVariable; // 인스턴스 변수
void method() {
int localvariable; // 지역 변수
}
}
클래스이름.메서드이름()
으로 호출 가능한 메서드🤔 의문점
Q. 왜 static 메서드 내부에서 인스턴스 멤버에 접근할 수 없을까?
Q. 어떻게 공통된 저장공간을 공유할 수 있는걸까?
흐름을 봤을 때, 이유는 '생성 순서'로 짐작이 간다. 그럼 정확히 어떤 이유로 static의 사용이 위와 같은 특성을 가지는지 자세히 알아보자!
Java의 작동원리에 대해 Java 실행원리 Deep Dive 글에서 너무 훌륭하게 정리해주셨다. 필요하면 모두 읽어보면 좋을 것 같고, 나는 여기서 static과 관련된 부분에만 집중해보려고 한다.
자바 프로그램 실행흐름을 따라가면서 static의 특징을 가지게 되었는지 살펴보자!
- Class Loader Subsystem에서 static의 생성 순서를 확인할 수 있고,
- Runtime Data Area에서 저장 공간의 차이에 대해 확인할 수 있다.
Class Loader Subsystem의 주목적은 필요한 class를 찾아 JVM 메모리에 로드하고 연결(link)하기 위함이다.
이 과정을 가볍게 살펴보자.
java 프로그램 실행에 필요한 클래스를 jvm에 로드한다.
.class 파일 내의 symbolic reference를 실제 메모리 주소로 변환하는 작업을 수행한다.
symbolic reference
우리가 코드를 작성하면서 사용한 class, field, method의 이름. Resole단계에서 class, field, method 그리고 constant pool의 references를 실제 메모리 주소로 변경한다.
Bytecode format
, version number
등을 확인한다.Static initializer block을 실행한다. 위 link prepare 단계에서는 static 변수가 default 값으로 초기화 되었으나, 이 단계에서 개발자가 지정한 값으로 static 변수가 설정된다. Initialize가 완료된 이후 main 메서드가 실행된다.
즉, 앞서 link단계에서 class와 static variable을 위한 메모리 공간이 준비되고, 초기화Ilitialize 단계에서 static 변수가 할당된 이후, main 메서드가 실행된다.
인스턴스 변수는 언제 준비될까? 다들 알다시피 main 메서드가 시행된 이후에 코드를 따라 내려가면서 인스턴스 변수들이 생성되고 소멸하게 된다.
즉, static 변수 생성 > main 메서드 실행 >인스턴스 변수 생성의 순서를 따르므로 static 내부에서 인스턴스 멤버에 접근할 수 없다!
static variable은 method area에, insatance variabe은 heap에 저장된다. 이때, 각 인스턴스에 포함된 static 변수들이 모두 Method Area에 할당된 같은 주소를 가리키고 있기 때문에, 값을 공유하며 일괄적으로 변경된다!
Heap영역은 Garbage Collector를 통해 관리가 되지만, Static 영역의 메모리는 관리되지 않는다. static 영역에 할당된 정보는 프로그램의 종료시 까지 메모리에 할당된 채로 존재하기 때문에, 너무 자주 사용해서는 안된다!