Java JVM 메모리 구조와 메모리 저장 방식

JVM의 메모리 구조

Java 프로그램이 실행될 때, JVM은 OS(운영 체제)로부터 해당 프로그램을 수행할 수 있도록 필요한 메모리를 할당받습니다. 할당받은 메모리를 JVM은 메모리 공간 효율성을 높이기 위해 용도에 따라 여러 영역으로 구분하여 관리합니다.

https://www.devkuma.com/docs/jvm/memory-structure/

  • method area / static area
    이 영역은 자바 프로그램에서 사용되는 클래스 정보클래스 변수(static), 상수가 저장되는 영역입니다. 클래스 로딩 시 이 영역에 저장되며, 모든 스레드에서 공유됩니다. 
    JVM은 특정 클래스가 사용되면 해당 클래스 즉, 자바 바이트 코드인 클래스 파일(*.class)를 읽어 들인 클래스와 인터페이스에 대한 런타임 상수 풀, 멤버 변수(필드), 클래스 변수(static 변수), 생성자와 메소드를 메소드 영역에 저장합니다.
  • heap
    객체가 저장되는 영역입니다. 힙 영역은 동적으로 할당된 메모리로, new 연산자를 사용하여 인스턴스가 생성되면, 해당 인스턴스의 정보를 힙 영역에 저장합니다. 모든 객체와 배열은 힙 영역에 생성됩니다.
  • stack
    메소드 호출 시 생성되는 지역 변수매개변수를 스택 영역에 저장합니다. 각 메소드 호출마다 새로운 스택 프레임이 생성되며, 메소드가 끝나면 해당 프레임이 사라집니다. 또한 생성한 객체의 참조 변수가 저장됩니다.
  • PC(Program Counter) register
    현재 실행 중인 스레드의 명령어 주소를 가리킵니다.
  • native method stack
    자바 외 언어로 작성된 네이티브 코드를 호출할 때 사용되는 Stack 영역입니다.

 

객체 생성 흐름

  1. 클래스 로딩: 클래스가 처음 호출되면 메소드 영역에 클래스 정보가 로드됩니다.
  2. 메모리 할당: 객체를 생성하면, JVM은 힙 영역에서 메모리를 할당하고 해당 객체의 주소를 반환합니다.
  3. 참조 변수에 할당: 반환된 객체의 주소는 스택 영역의 참조변수에 저장됩니다.
  4. 참조를 통해 객체 접근: 참조변수를 통해 객체의 필드와 메소드를 사용할 수 있습니다.
  5. 메모리 제거: 스택 영역의 변수가 힙 영역의 인스턴스를 참조하고 있다가 더 이상 참조하지 않게 되면, JVM의 GC(Garbage Collection)을 통해 인스턴스는 제거됩니다.

 

코드로 보는 저장 예시

class Car {
    static int totalCarsCount = 0;  // 클래스 변수, method area에 저장
    String model;                   // 인스턴스 변수, heap area에 저장

    Car(String model) {
        this.model = model;
        totalCarsCount++;
    }

    void drive() {
        System.out.println(model + " 운전");  // 메소드 호출 시 스택에 저장
    }
}

public class Main {
    public static void main(String[] args) {
        Car car1 = new Car("Tesla");  // 참조변수 car1은 stack에, 객체는 heap에 저장
        Car car2 = new Car("BMW");    // 참조변수 car2는 stack에, 객체는 heap에 저장
        car1.drive();  // "Tesla 운전" 출력
        car2.drive();  // "BMW 운전" 출력
    }
}
  • Car 클래스의 totalCarsCount 변수는 메소드 영역에 저장되어 모든 객체가 공유됩니다.
  • car1과 car2는 stack 영역에 저장된 참조 변수로, 각자의 heap 영역에 있는 Car 객체를 가리킵니다.