[Java] - JVM 런타임 데이터 영역

chancehee·2025년 1월 15일
0

자바

목록 보기
13/13
post-thumbnail

[ 개요 ]

메모리 관리 측면에서 C, C++ 개발자는 객체 각각의 생애주기와 메모리 관리를 신경 쓰며 개발해야 한다.
반면에, Java 개발자는 JVM이 제공하는 자동 메모리 관리 메커니즘 덕에 비즈니스 로직에 집중할 수 있다.

하지만, JVM에게 통제권을 위임 했기 때문에 문제가 한번 터지면 가상 머신의 메모리 관리 방식을 이해하지 못하는 한 해결하기가 상당히 어렵다.

JVM이 관리하는 다양한 메모리 영역을 학습하는 것을 목표로하며 글을 작성한다.

[ 런타임 데이터 영역 ]

구성 요소는 아래와 같다.

  • 프로그램 카운터(pc) 레지스터
  • 자바 가상 머신 스택
  • 네이티브 메서드 스택
  • 메서드 영역

[ 프로그램 카운터 레지스터 ]

현재 실행 중인 스레드의 바이트코드(명령어의 주소)를 저장하는 작은 메모리 영역

특징

  • 바이트코드 인터프리터에 의해 변경되는 대상으로, 프로그램의 제어 흐름, 분기, 순환, 점프 등을 표현하는 값을 갖고 있다.
  • [1]스레드별 데이터 영역
  • <<자바 가상 머신 명세>>에서 OutOfMemoryError 조건이 명시되지 않은 유일한 영역

관련내용

  • [1-1]: JVM에서의 멀티스레딩은 CPU 코어를 여러 스레드가 교대로 사용하는 방식으로 구현되기 때문에 특정 시각에 각 코어는 한 스레드의 명령어만 실행하게 된다.
    따라서 스레드 전환 후 이전에 실행하다 멈춘 지점을 정확하게 복원하려면 스레드 각각에는 고유한 프로그램 카운터가 필요하다.
  • [1-2]: 스레드가 자바 메서드를 실행 중일 때는 실행 중인 바이트코드 명령어의 주소가 프로그램 카운터에 기록된다.
    네이티브 메서드를 실행 중일 때 프로그램 카운터 값은 Undefined다.

[ 자바 가상 머신 스택 ]

자바 바이트코드로 구현된 메서드 호출과 실행을 지원하는 메모리 영역

특징

  • 각 메서드가 호출될 때마다 JVM이 만든 [1]스택 프레임을 자바 가상 머신 스택에 push하고, 메서드가 끝나면 pop하는 일을 반복한다.
  • 스레드별 데이터 영역
  • 개발자들이 일반적으로 생각하는 자바 가상 머신 스택은 [2]지역 변수 테이블을 가리키는 경우가 많다.
  • <<자바 가성 머신 명세>>는 스택 메모리 영역에서 두 가지 오류가 발생할 수 있도록 정의했다.
    • StackOverflowError
    • OutofmemoryError

관련내용

  • [1]: 스택 프레임은 지역 변수 테이블, 피연산자 스택, 동적 링크, 메서드 반환값 등의 정보를 저장한다.
  • [2-1]: 지역 변수 테이블에는 자바 가상 머신이 컴파일타임에 알 수 있는 다양한 기본 데이터 타입, 객체 참조, 반환 주소 타입을 저장한다.
  • [2-2]: 지역 변수 테이블을 구성하는 데 필요한 데이터 공간은 컴파일 과정에서 할당된다.

[ 네이티브 메서드 스택 ]

JVM 외부에서 구현된 네이티브 메서드의 호출과 실행을 지원하는 메모리 영역

특징

  • 자바 가상 머신 스택과 비슷한 역할을 한다. 차이점은 네이티브 메서드를 실행할 때 사용한다.
  • <<자바 가상 머신 명세>>는 네이티브 메서드 스택에서 메서드를 어떤 구조로 어떻게 표현해야 하는지와 관련하여 아무것도 명시하지 않았다. 따라서 가상 머신 구현자가 원하는 형태로 자유롭게 표현할 수 있다.
    ex) 자바 가상 머신 스택 + 네이티브 메서드 스택.(분리하지 않고 합쳐서 사용.)
  • <<자바 가성 머신 명세>>는 스택 메모리 영역에서 두 가지 오류가 발생할 수 있도록 정의했다.
    • StackOverflowError
    • OutofmemoryError

[ 힙 ]

JVM에서 런타임 동안 생성된 객체 인스턴스 저장하며, Garbage Collector에 의해 관리되는 동적 메모리 영역

특징

  • 자바 애플리케이션이 사용할 수 있는 가장 큰 메모리
  • [1]모든 스레드가 공유하며, 가상 머신이 구동될 때 만들어진다.
  • <<자바 가상 머신 명세>에는 "모든 객체 인스턴스와 배열은 힙에 할당된다." 라고 적혀 있다.
  • [2]GC(Garbage Collector)가 관리하는 메모리 영역이다.
  • <<자바 가상 머신 명세>>에 따르면 자바 힙은 물리적으로 떨어진 메모리에 위치해도 상관없으나 [3]논리적으로는 연속되어야 한다.
  • 크기를 고정할 수도, 확장할 수도 있게 구현할 수 있다.(-Xmx와 -Xms 매개 변수 사용)
    새로운 인스턴스에 할당해 줄 힙 공간이 부족하고 힙을 더는 확장할 수 없다면 OutOfMemoryError를 던진다.

관련내용

  • [1]: 내부적으로 객체 할당 효율을 높이고자 스레드 로컬 할당 버퍼 여러 개로 나뉜다. 자바 힙을 다시 작게 구분하는 주요한 목적은 오직 메모리 회수와 할당을 더 빠르게 하기 위함이다.
  • [2-1]: 메모리 회수 관점에서 대다수 현대적인 가비지 컬렉터는 '세대별 컬렉션 이론'을 기초로 설계됐다.
  • [2-2]: 일반적으로 신세대, 구세대, 영구 세대, 에덴, 생존자 공간 등으로 나뉜다.
    ex) 대표적으로 'G1 GC를 사용하는 핫스팟 JVM'에서 전통 세대 구분을 따른다.
  • [3-1]: 파일을 저장할 때 디스크 공간을 활용하는 방식과 같다.(파일 각각은 논리적으로 연속된 공간에 저장됨)
  • [3-2]: 대다수 가상 머신이 큰 객체(주로 배열 객체)는 물리적으로 연속된 메모리 공간을 사용하도록 구현한다. 저장 효율을 높이고 구현 로직을 단순하게 유지하기 위해서다.

[ 메서드 영역 ]

클래스와 인터페이스에 대한 정보, 상수, 정적 변수 등을 저장하는 런타임 메모리 영역

특징

  • 모든 스레드가 공유한다.
  • JDK 7까지 핫스팟 가상 머신 개발 팀은 GC의 수집 범위를 메서드 영역까지 확장하기로 결정하여 영구 세대를 사용했지만, 여러 단점이 있어 JDK 8 부터는 네이티브 메모리에 메타스페이스를 구현했다.
  • <<자바 가상 머신 명세>>는 메서드 영역에 제약을 거의 두지 않았다.
  • GC 회수 대상인 [1]런타임 상수 풀과 타입은 회수 효과가 상대적으로 매우 작다.
    (GC를 하지 않아도 괜찮은 수준 이지만, 회수가 꼭 필요한 상황도 있다.)
  • <<자바 가상 머신 명세>>에 따르면 메서드 영역이 꽉 차서 필요한 만큼 메모리를 할당할 수 없다면 OutOfMemoryError를 던진다.

관련내용

  • [1-1]: 런타임 상수 풀은 메서드 영역의 일부다. 클래스 버전, 필드, 메서드, 인터페이스 등 클래스 파일에 포함한 설명 정보에 더해 컴파일타임에 생성된 다양한 리터럴과 심벌 참조가 저장된다.
  • [1-2]: 런타임 상수 풀은 메서드 영역에 속하므로, 메서드 영역을 넘어서까지 확장될 수는 없다. 그래서 상수풀의 공간이 부족하면 OutOfMemoryError를 던진다.
참고자료

0개의 댓글