JVM의 메모리 영역과 static

Soobin Kim·2024년 3월 29일

Java

목록 보기
18/47

JVM의 메모리 영역

JVM(Java Virtual Machine)은 자바 프로그램이 실행되는 환경을 제공하며, 이를 위해 여러 메모리 영역을 사용한다. 각 영역은 특정 유형의 데이터를 저장하고 관리하는 역할을 한다.


  • Heap: 모든 객체와 배열이 저장되는 곳이며, Garbage Collector가 관리하는 영역. 모든 JVM 스레드에 의해 공유되는 공간이다.

  • Stack: 메소드 호출과 지역 변수 등의 정보를 저장하고 각 스레드마다 별도의 스택을 가진다.

    • 메서드가 호출되면 메서드의 기계어 코드를 할당받고 메서드가 실행되는 메모리 공간이다.
    • 호출된 메서드는 LIFO(Last-In-First-Out) 구조로 관리된다.
    • Program Counter(PC)는 현재 수행 중인 프로그램 위치를 가리킨다. 따라서 현재 프로그램이 실행되고 있는 상태를 파악할 수 있도록 실행 중인 메모리 공간이다.
    • PC가 바닥을 가리키면(stack에 아무 것도 없다면) 프로그램이 종료됨.
  • Method Area(static-zone): 클래스 정보, 상수, static 변수 등이 저장되는 영역으로 모든 스레드가 공유하는 영역이다. 메서드의 바이트 코드가 할당되며 static-zone과 non-static-zone으로 나뉜다.

  • Runtime Constant Pool: 클래스 또는 인터페이스의 상수 및 동적 참조를 저장하는 영역으로, 클래스 파일의 constant_pool 테이블의 런타임 표현이다.


Static

static 멤버는 클래스에 속하며, 모든 인스턴스가 공유하는 변수나 메소드이다.

  • static 멤버는 클래스가 메모리에 로드될 때 생성되고, 프로그램 종료까지 유지된다.
  • static 멤버는 객체 생성 없이 클래스 이름을 통해 직접 호출할 수 있다.
  • static 멤버들은 클래스를 사용하는 시점에서 한 번 자동으로 정해진 메모리(static-zone) 위치에 로드된다.

클래스 로더(class loader)

JVM의 클래스 로더는 자바 가상 머신(JVM)에서 컴파일된 자바의 클래스 파일(*.class)을 동적으로 로드하고 JVM의 메모리 영역인 Runtime Data Areas에 배치하는 작업을 수행한다.

  • class 파일의 로딩 순서는 Loading -> Linking -> Initialization이다.

    Loading: 실행할 클래스 파일을 가져와 JVM 메모리에 로드.

    • 한 번에 수행하는 것이 아니라 어플리케이션에서 필요한 경우 동적으로 메모리에 적재!.

    Linking: 클래스 파일을 사용하기 위해 검증

    Initialization: 초기화, 즉 클래스 변수들을 적절한 값으로 초기화.


main메서드의 동작

메서드가 호출되면, JVM의 Call Stack Frame Area(스택 영역)에 해당 메서드를 위한 프레임이 생성된다. 즉, 각 메서드 호출마다 고유의 프레임이 생성되어, 메서드의 실행 상태를 관리한다. 이 프레임 내에는 메서드의 지역 변수, 매개변수, 리턴 값 등이 저장된다.

  1. static 메서드와 변수는 클래스가 로드될 때 컴파일된 기계어 코드로 static-zone에 로드된다.
  2. static 메서드가 호출되면 JVM이 static-zone에서 호출된 메서드를 Call Stack Frame Area(Stack Area)에 push(기계어 코드를 넣고)한 뒤 동작을 시작한다.

none static과 static의 메모리 로드 차이

  • static 멤버들은 클래스 로딩 시에 Method Area에 로드되어 클래스의 모든 인스턴스들이 공유.
  • main 메서드가 호출되고 new 연산자에 의해 Test 클래스의 인스턴스가 생성될 때, 인스턴스 멤버들은 각 인스턴스의 heap 메모리에 저장된다. 이때, static 멤버들은 이미 Method Area에 로드되어 있기 때문에 다시 로드되지 않으며 인스턴스 멤버들(non-static 멤버들)은 각 인스턴스마다 별도의 메모리 공간에 저장된다.

모든 멤버가 static일 때

public class StaticAll{
     private StaticAll(){}
     public static ...
     public static ...
     public static ...
}

  • java API에서는 private 생성자를 가진 클래스가 있다(System, Math 등)
  • static 멤버는 클래스명.멤버명으로 접근할 수 있지만, 인스턴스 생성 후 접근할 수도 있다. 하지만 이는 바람직한 접근법이 아니므로 모든 멤버가 static일 때 후자의 접근을 막기 위해서 의도적으로 생성자를 private 접근제어자를 이용하여 인스턴스를 생성하지 못하게 할 수 있다.

인스턴스로 접근하여 static 멤버를 사용하는 경우에도 실제로는 Method Area에 등록된 static 멤버를 참조하게 된다. 이는 컴파일러가 해당 멤버를 클래스명을 통해 접근하는 것으로 변환하여 실행하기 때문이다. 즉, 인스턴스로 접근하더라도 실제로는 Method Area에 등록된 static 멤버를 가져오게 된다.


static과 static final 멤버의 차이

  • Static 멤버를 호출하면 클래스가 로드되지만, static final 멤버를 호출할 경우 해당 클래스는 로드되지 않는다(∵ 상수는 JVM의 Method Area의 Constant Pool에 따로 저장되어 관리되기 때문).

예시

 public class Test{
   // ...
 
   public static int a=1;
   public static void testMethod(){System.out.println("test");}
    
   // ...
}
public class Main{
   public static void main(String[] args){Test.testMethod();}
}
  • Test 클래스의 정적 메서드(testMethod())가 호출되면 클래스 로딩 과정에서 Test 클래스의 모든 정적 멤버(static member)가 메모리에 로드된다.

  • 이는 해당 클래스의 정적 멤버가 호출되는지 여부와 관계없이 발생하므로 atestMethod()와 같은 클래스인 Test 클래스의 멤버이므로 함께 메모리에 로드된다.

정적 변수(static variable) a의 로딩

클래스 로더(Class Loader)가 Test 클래스를 로드하면서 해당 클래스 파일을 분석. 분석 과정에서 a라는 정적 변수를 인식하고, 이를 메서드 영역(Method Area)에 할당. 변수의 타입과 초기값이 함께 저장된다.

정적 메서드(static method) testMethod()의 로딩

클래스 로더가 Test 클래스를 분석하면서 testMethod()라는 정적 메서드를 인식하고, 이를 메서드 영역에 할당. 메서드의 바이트 코드와 메서드 시그니처(리턴 타입, 매개변수 등)가 함께 저장된다.

0개의 댓글