[Java] JVM 메모리 구조 파헤쳐 보기 (Static, Stack, Heap)

JVM은 운영체제 위에서 실행될 때 각각의 운영체제로부터 메모리를 할당 받고

그 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.

여기서 메모리 공간은 크게 Method 영역, Heap 영역, Stack 영역으로 구분되고 데이터 타입에 따라 각 영역에 나눠서 할당 된다.

이번 포스팅에서는 이 영역에 대해 파헤쳐볼 예정이다.

 

참고로 JVM 메모리 구조를 파헤치기전에 JVM이 뭔지 알면 더 좋다.

JVM에 대한 내용은 아래 포스팅을 참고하자.

 

[Java] JVM이란? 구조와 특징에 대해 알아보자.

컴퓨터가 어떤 프로그램을 실행하려면, 그 프로그램이 컴퓨터의 언어 (기계어)로 작성되어야 한다. 하지만 우리는 복잡하고 해석하기 어려운 기계어를 직접 작성하기보다는 코드를 작성한다.

hstory0208.tistory.com

 

Method(Static) 영역

  • JVM이 동작하고 클래스가 로딩될 때 생성되며 JVM이 읽어들인  클래스 변수(Static 변수), 생성자(constructor)와 메소드(method) 등을 저장하는 공간이다.
  • 모든 스레드에 공유되어 어느 곳에서나 접근 가능하다.
  • Method(Static) 영역의 데이터는 프로그램이 종료될 때 까지 메모리에 남아있다.
  • Method(Static) 영역에 있는 데이터들은 프로그램이 종료될 때까지 어디서든 사용 할 수 있다.
  • 프로그램이 종료될 때 까지 남아있기 때문에, Method(Static)에 저장되는 데이터가 너무 과하지 않도록 주의해야한다.

 

public class Car {
    private static final int NAME_LENGTH_COND = 5;

    private final String name;
    private int position;

    public Car(final String name, final int position) {
		    validateNameLength(name);
        this.name = name;
        this.position = position;
    }

    public void move() {
        position++;
    }

    private void validateNameLength(final String name) {
        if (name.length() > NAME_LENGTH_COND) {
            throw new IllegalArgumentException("이름은 5글자까지만 가능합니다.");
        }
    }
}

위와 같은 코드가 있을 때 Method(Static)영역에 저장되는 데이터는 뭐가 될까?

 

Method(Static)영역에 저장되는 데이터는 위와 같다.

인스턴스 변수인 name과 position은 method 영역에 들어가지 않고

클래스 영역의 static 변수, 생성자, 메서드가 저장되는 것을 볼 수 있다.

 

Stack 영역

  • 메소드 내에서 정의하는 기본 자료형(Primitive Type - int, double, byte, long, boolean 등)에 해당되는 지역변수, 매개변수의 데이터 값이 저장되는 공간이다.
  • 각 스레드마다 독립적인 Stack 영역을 가진다.
  • 메소드가 실행될때 Stack 영역 안에 스택 프레임이 생기고 Stack 영역안의 메소드를 호출한다.
  • 메소드가 호출 될 때 메모리에 할당되고 메서드가 종료되면 메모리에서 사라진다.

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

하나의 메서드에 필요한 메모리 덩어리를 묶어서 스택 프레임(Stack Frame)이라고 한다.

하나의 메서드당 하나의 스택 프레임이 필요하며, 메서드를 호출하기 직전 스택 프레임을 Stack 영역에 생성한 후 메서드를 호출하게 된다. 스택 프레임에 쌓이는 데이터는 메서드의 매개변수, 지역변수, 리턴값 등이 있다.

만일 메서드가 종료되면 ( 닫는 괄호 "}"까지 코드가 실행되면) 제거된다

 

만약 위의 Car 객체가 생성되고 move() 메서드를 실행한다면 Stack 영역에 어떤식으로 데이터가 쌓일까?

메소드가 호출될 때마다 해당 메소드의 실행에 필요한 정보를 담은 스택 프레임이 스택 영역에 생성되고

메소드 실행이 완료되면 해당 스택 프레임은 스택에서 제거되어 메모리가 해제된다.

 

참고로 Car 생성자 스택 프레임에 저장된 name과 position은 인스턴스 필드가 아니라 파라미터 데이터가 저장된 것이다.

인스턴스 필드는 Heap 영역에 저장된다.

 

Heap 영역

  • 참조형(Reference Type) 데이터 타입을 갖는 객체(인스턴스), 배열 등과 같은 런타임 데이터를 저장하기 위한 영역이다.
  • Stack 영역과 다르게 단 하나의 heap 영역만 생성되어 어느곳에서나 접근 가능하다. (공유됨)
  • 단, Heap 영역에 있는 객체들을 가리키는 참조를 위한 주소값은 Stack에 포함된다.
  • Heap 영역은 Stack 영역과 다르게 보관된 데이터가 호출이 끝나더라도 삭제되지 않고 유지된다.
  • 어떤 참조 변수도 Heap 영역에 있는 인스턴스를 참조하지 않게 된다면, GC(가비지 컬렉터)에 의해 메모리에서 청소된다.

 

위에서 작성한 Car 코드를 아래처럼 사용했을 때 어떤식으로 Heap에 저장되고 제거되는지 살펴보자.

public class Main {

    public static void main(String[] args) {
        Car car = new Car("kaki", 0);
        car.move();
    }
}

 

1. Car 객체 생성

Stack 영역

Car 생성자와 생성자 내부에서 사용되는 validateNameLength() 메서드가 스택 영역에 할당되고

Heap 영역에 생성된 객체가 참조할 주소 값을 가진다.

 

Heap 영역

Car 클래스의 인스턴스 변수가 저장되고 Stack 영역의 지역변수 car와 주소값으로 연결된다.

 

car.move() 실행

이제 만들어진 car 객체를 move() 시키면 어떻게 될까?

car 객체 내부에 정의된 move() 메서드를 호출하면서 이 메서드도 Stack 영역에 새로운 스택 프레임으로 추가된다.

이 때 this 라는 암묵적인 변수가 자동 생성되게 되는데, 이 this 변수는 자동으로 힙 영역에 있는 Car 객체를 가리키게 된다.

따라서 move() 메소드 안의 코드 position++이 동작하면서 힙 영역에 있는 인스턴스 변수 position 값이 변하게 된다.

 

main() 메서드 실행 종료

main() 메서드 내부의 마지막 코드가 실행되고 닫는 중괄호 “}” 에 도달하면 종료된 main() 스택 프레임이 메모리에서 제거된다.

하지만 Heap 영역에는 Car 객체 데이터가 여전히 남아있다.

 

GC의 Heap 영역  청소

가비지 컬렉터는 힙 영역에 참조되지 않고 남아버린 객체들 중 더 이상 참조되지 않는 객체를 자동으로 검색해 제거한다.

 

Stack 영역과 Heap 영역의 차이점 정리

  Heap 영역 Stack 영역
접근 하나만 생성되고 모든 스레드에 공유되어
어디서든 접근 가능하다.
(Method 영역도 동일)
스레드 별로 독립된 영역을 가져
다른 스레드가 접근할 수 없다.
저장되는 데이터 참조형(Reference Type) 데이터 타입을 갖는 객체(인스턴스)는 항상 Heap에 저장된다. primitive 타입의 지역변수와 매개변수
그리고 Heap 영역에 있는 객체 참조 값 주소를 갖는다.
생명주기 GC의 제거 대상이 되기전까지 남아있는다. 메서드가 종료되면 해당 메서드 스택이 제거된다.
메모리가 가득 찰 경우 OutOfMemoryError StackOverFlowError
크기 크기를 실행 전과 실행 중에 조정할 수 있다. 비교적 작으며 스레드마다 고정된 크기를 가진다.
(JVM이나 OS에 따라 정해진 크기가 다르다.)
속도 더 복잡한 메모리 관리(예: 가비지 컬렉션)를 요구하기 때문에 Stack에 비해 느리다. LIFO(Last In First Out) 방식으로 동작해 메모리 관리가 단순하여 Heap에 비해 빠르다.