Runtime Data Area

이건희·2025년 2월 22일

Runtime Data Area

JVM의 메모리 영역으로, 자바 애플리케이션을 실행할 때 사용되는 데이터를 적재하는 영역이다.
이 영역은 크게 Method Area, Heap Area, Stack Area, PC Register, Native Method Stack으로 나뉜다.


Method Area (메소드 영역)

클래스 파일의 정보(메타데이터), static 변수, 메서드 정보, 생성자, 상수(Constant Pool) 등을 저장하는 영역.

  • JVM이 실행될 때 Class Loader에 의해 클래스를 로드하여 Method Area에 저장.

  • 모든 스레드가 공유한다 !

  • JVM이 종료될 때까지 유지되며, 오래 사용될 경우 OutOfMemoryError 발생 가능.

  • static final 변수는 컴파일 타임에 값이 확정되면 Method Area(Constant Pool)에 저장되지만, 일반 final 인스턴스 변수는 Heap에 저장됨.


Heap Area (힙 영역)

new 키워드로 생성된 객체와 배열이 저장되는 영역 (동적 할당된 객체들의 저장소).

  • Method Area에 로드된 클래스만 객체 생성 가능.

  • Garbage Collector(GC)가 참조되지 않는 객체를 자동으로 제거.

  • Heap은 JVM에서 가장 큰 메모리 공간이며, 여러 개의 스레드가 공유하는 영역이다.

    • 즉, 여러 스레드가 공유해서 이 영역을 참조할 수 있다 !

Stack Area (스택 영역)

지역 변수, 매개 변수, 리턴 값, 연산에 사용되는 임시 값 등이 저장되는 영역.

  • 메소드가 호출될 때마다 스택 프레임(Stack Frame)이 생성되고, 메소드 실행이 끝나면 해당 스택 프레임이 제거됨.

스택 프레임(Stack Frame)이란?

  • 각 메소드 실행을 위한 독립적인 공간으로, 해당 메소드의 지역 변수, 매개 변수, 연산 결과, 리턴 값 등이 저장됨.

  • 호출된 메소드가 완료되면 해당 스택 프레임이 제거되며, 메모리가 자동으로 관리됨.

  • 이러한 구조 덕분에 메소드가 재귀 호출되면 스택이 계속 쌓이게 되며, 너무 깊어지면 StackOverflowError가 발생할 수 있음.

  • 기본 타입(Primitive Type) 변수(int, double, boolean 등)객체의 참조 변수(주소)가 저장됨.

  • 예시

    int a = 10;  // 'a'는 Stack에 저장됨
    Person p = new Person(); // 'p'는 Stack에 저장, 실제 객체는 Heap에 저장됨

  • 각 스레드마다 독립적인 공간을 가진다 !

  • JVM은 스레드마다 개별적인 Stack을 생성하고, 그 안에서 메소드 호출이 발생할 때마다 스택 프레임(Stack Frame)이 추가됨.
    즉, 여러 개의 스레드가 동시에 실행되더라도 각 스레드는 자신만의 스택을 사용하므로 데이터 충돌 없이 독립적으로 동작함.

예시

class Task implements Runnable {
    @Override
    public void run() {
        int localVar = 0; // 각 스레드마다 독립적인 공간
        localVar++;
        System.out.println(Thread.currentThread().getName() + " - localVar: " + localVar);
    }
}

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Task(), "Thread-1");
        Thread t2 = new Thread(new Task(), "Thread-2");

        t1.start();
        t2.start();
    }
}

//실행 결과
Thread-1 - localVar: 1
Thread-2 - localVar: 1

PC Register (PC 레지스터)

JVM이 현재 실행 중인 바이트코드 명령어의 주소(메모리 위치)를 저장하는 공간

  • 각 스레드마다 독립적인 PC Register를 가짐

  • 역할

    • 메서드가 실행되면, 해당 메서드의 바이트코드 명령어를 하나씩 실행해야 하는데, JVM은 현재 실행 중인 명령어의 주소(Program Counter, PC)를 저장해야 함.

    • 이를 위해 각 스레드는 자신의 PC Register를 가지며, 현재 실행 중인 바이트코드의 위치를 추적함.

    • 즉, 현재 실행 중인 코드의 다음 실행 위치를 저장하는 역할.

  • 멀티스레드 환경에서 중요한 이유

    • 여러 개의 스레드가 동시에 실행되면, 각각 실행하는 코드의 위치가 다를 수 있음.

    • 각 스레드는 자신만의 PC Register를 가지므로, 서로 다른 명령어를 실행할 수 있음.

    • 만약 모든 스레드가 하나의 PC Register를 공유했다면, 어떤 스레드가 실행되었는지 알 수 없어서 프로그램이 정상적으로 동작하지 않음.

쉽게 말하면, PC Register는 JVM이 "지금 어디까지 실행했는지"를 추적하는 역할을 하는 공간이고, 각 스레드마다 독립적으로 관리됨.


Native Method Stack (네이티브 메소드 스택)

Java가 아닌 네이티브 코드(C, C++ 등)로 작성된 메서드를 실행할 때 사용하는 메모리 영역
JNI (Java Native Interface)를 통해 네이티브 코드를 호출할 때 활용됨.

  • 역할

    • Java는 기본적으로 JVM 위에서 실행되지만, 특정 성능 최적화나 OS 기능 활용을 위해 네이티브 코드(C, C++ 등)를 호출할 필요가 있음.
    • 이때, 네이티브 코드 실행을 위한 별도의 스택이 필요함.
    • 네이티브 메서드를 호출하면 JVM이 Native Method Stack을 생성하고, C/C++ 코드에서 사용하는 스택 프레임을 쌓음.
  • 만약 네이티브 메서드를 사용하지 않는다면?

    • 일반적인 Java 애플리케이션에서는 거의 사용되지 않음.
    • 하지만 Java가 OS와 직접 상호작용할 필요가 있는 경우(예: 파일 시스템, 네트워크, 그래픽 처리 등) JNI를 통해 네이티브 메서드를 호출할 수 있음.

예제

class NativeExample {
    // 네이티브 메서드 선언 (C 코드에서 구현)
    public native void sayHello();

    static {
        System.loadLibrary("NativeLib"); // 네이티브 라이브러리 로드
    }
}

위 코드에서 sayHello() 메서드는 Java가 아닌 C 코드에서 구현되며, JVM은 이 메서드를 실행할 때 Native Method Stack을 사용함.

쉽게 말하면, 네이티브 메서드 스택은 Java가 아닌 C, C++ 같은 네이티브 코드를 실행할 때 필요한 스택 공간


Heap vs Stack 비교

구분HeapStack
저장 대상객체(인스턴스), 배열지역 변수, 매개 변수, 참조 변수
관리 방식GC(Garbage Collector) 자동 관리메소드 호출 시 할당, 종료 시 해제
속도상대적으로 느림 (GC 개입)상대적으로 빠름
메모리 할당동적 할당 (new 키워드 등)정적 할당 (컴파일 시 결정)
예제new Counter(); 객체 저장Counter c; 참조 변수 저장
  • static final 상수는 Method Area의 Constant Pool에 저장될 수도 있지만, 일반 final 인스턴스 변수는 Heap에 저장됨.

  • String 객체는 리터럴이면 Method Area, new String("abc")이면 Heap에 저장됨.

  • Heap의 객체를 가리키는 레퍼런스 변수는 Stack에 저장됨.

  • Stack은 메소드 단위로 메모리를 관리하며, 호출이 끝나면 자동으로 해제되므로 메모리 효율이 높음.


사진 출처

profile
백엔드 개발자가 되겠어요

0개의 댓글