자바 - 그래서 `static` 변수는 어디에 저장되는가?

this-is-spear·2022년 12월 23일
4

자바는 즐거워

목록 보기
1/1

static 변수는 어디에 저장되나요?

메서드 영역의 static pool에 저장됩니다...?

모의 면접에서 질문 받았을 때, 검색으로 나온 답변처럼 메서드 영역의 static pool에 저장됩니다. 이라고 대답했습니다. 솔직하게 검색한 답변 맞습니다. 😢

슬프지만… 메모리 영역 관련 질문이 나오면 언뜻 대답하기 어려웠습니다. 그래서 과오를 잡기 위해 static 만 선언된 변수와 staticfinal이 선언된 변수로 나눠서 메모리를 할당받고 값 초기화, 호출, 소멸 동작을 정리했습니다.

목차

  • Class Metadata
  • static 호출 동작
  • static and final 호출 동작
  • 소멸 동작
  • 차이점
  • 단점
  • 예외 상황
  • 그래서 정리한 답변은

Class Metadata in Method Area

메서드 영역에 저장되는 정보 단위를 클래스 메타데이터라 합니다. 클래스 메타데이터에는 Type 정보, Constant Pool, Field 정보, Method 정보, Class Variables, Reference to ClassLoader, Reference to Class 정보가 포함됩니다.

그 중 스태틱 변수 호출 동작을 확인하기 위해서 Constant PoolClass Variables에 대해 알아보겠습니다.

  • Constant Pool
    • 클래스에 선언된 상수들의 symbolic 참조 정보를 가집니다. 프레임 데이터에서 해당 데이터의 영역 정보를 가져와 메서드를 수행합니다.
  • Class Variable
    • 상수 값인 클래스 변수를 관리합니다. 모든 인스턴스에 공유되며 인스턴스 없이 접근 가능합니다. final 클래스 변수는 상수로 치환되어 Method AreaConstant Pool에 값을 복사합니다

static ‘만’ 선언된 변수 호출 동작

메모리 할당과 초기화 단계

클래스로더에서 클래스 정보를 메모리에 적재하는 과정 중 static 변수의 초기화 과정을 정리했습니다.

  1. 클래스로더에 의해 메모리를 할당받고 static initializer에 의해 값을 초기화합니다.
  2. 메모리 영역의 class variable 영역에 변수 값이 저장됩니다.
  3. 클래스의 constant poolclass variable참조 값이 저장됩니다.

호출 단계

호출 단계에서는

  1. Constant Pool의 메모리 공간의 시작 지점을 조회합니다.
  2. Constant Pool에 저장된 b의 참조 값을 읽습니다.
  3. Class Variables에서 b의 실제 값을 읽습니다.

staticfinal이 선언된 변수 호출 동작

메모리 할당과 초기화

마찬가지로 클래스로더에서 클래스 정보를 메모리에 적재하는 과정 중 static 변수의 초기화 과정을 정리했습니다.

  1. 변수는 메모리를 할당받고 static initializer에 의해 값을 초기화합니다.
  2. 메모리 영역의 class variable 영역에 실제 값이 저장됩니다.
  3. 클래스의 constant poolclass variable 영역의 실제 값이 복사됩니.

호출 단계

  1. Constant Pool의 메모리 공간의 시작 지점을 조회합니다.
  2. Constant Pool에 저장된 b의 실제 값을 읽습니다.

소멸 동작

class metadata가 저장되는 method area(metaspace)는 heap 영역에서 관리되기 때문에 static 변수를 참조하지 않는 상황이 온다면 GC 대상이 될 수 있습니다. (결론은 class metadata가 GC 대상이 되면 자동적으로 static 변수들도 GC 대상이 된다고 볼 수 있습니다.)

왜 차이가 발생하는 걸까?

상수 풀에 복사된 값을 수정하는 연산이 진행되면 클래스 메타 데이터를 갱신해야하고, 데이터 정합성을 위해 생기는 락에 의해 성능이 떨어질 수 있기 때문에 변경 가능성이 있는 변수를 따로 관리한다고 볼 수 있습니다.

그 밖의 단점은?

가변성을 가진 상수는 모든 스레드에서 접근하기 때문에 동시성 이슈가 발생할 수 있습니다. 동시성을 제어해 thread-safe 하게 구현한다면 성능이 떨어지게 됩니다.

예외 상황 - static 변수가 바로 초기화되지 않는 경우도 있습니다.

static 변수들이 초기화되는 시점이 항상 클래스의 인스턴스가 생성되는 시점이라고 볼 수 없습니다. static 변수를 lazy loading 해 효율적으로 메모리를 사용할 수 있습니다.

12.4.1. When Initialization Occurs -
Oracle JVM Spec

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.
  • static method declared by T is invoked.

static 메서드는 호출 시점에 초기화되는 방식을 이용해 static variables가 선언된 inner class instance 생성을 제어하면 static 변수의 초기화 시점을 원하는 순간으로 조절할 수 있습니다.

다시 물어본다면 static 선언된 변수는 어디에 저장되나요?

static 선언된 변수는 Class Variables(또는 static pool)에 저장됩니다. static 변수 중, final이 선언된 변수는 constant pool에 값이 복사되어 값이 조회될 때의 instruction을 줄여 성능을 높이고 있습니다. 추가로 lazy loading으로 static 변수 초기화 시점을 조절해 메모리를 효율적으로 사용할 수 있습니다. 라고 답변할 수 있습니다.

정리

  1. static 만 선언된 경우 class variables 정보에서 값을 참조합니다.
  2. staticfinal 이 선언된 경우 constant pool 에서 값을 참조합니다.
  3. static 만 선언된 경우 동시성 이슈가 발생할 수 있습니다.
  4. staticfinal을 동시에 사용해 thread-safe하고 성능 이슈도 발생하지 않는 구현을 합시다.
  5. lazy loading으로 static 변수 초기화 시점을 조절해 메모리를 효율적으로 사용할 수 있습니다.
profile
익숙함을 경계하자

0개의 댓글