개발언어/Java

JVM 메모리 구조

eodevelop 2025. 5. 13. 12:58
반응형

자바 가상 머신(JVM)은 자바 프로그램을 실행할 때 메모리를 효율적으로 관리합니다. 자바 개발자는 메모리 관리를 직접 하지 않아도 되지만, 메모리 누수나 오버플로 같은 문제가 생기면 JVM의 작동 방식을 알아야 해결할 수 있습니다. 이 글에서는 JVM이 메모리를 어떻게 나누고 사용하는지 설명합니다.

 

JVM은 메모리를 두 가지로 나눕니다:

  1. 스레드마다 따로 가지는 영역 (스레드 프라이빗)
  2. 모든 스레드가 함께 사용하는 영역

각 영역의 역할과 특징을 하나씩 알아보겠습니다.


메모리 영역을 간단히 그려본 이미지

1. 스레드마다 따로 가지는 메모리 영역

스레드마다 독립적인 메모리 공간은 각 스레드가 작업할 때 필요한 데이터를 저장합니다. 다른 스레드와 간섭 없이 안전하게 관리됩니다.

1) 프로그램 카운터 (PC 레지스터)

  • 역할: 현재 스레드가 실행 중인 명령어의 위치를 기록합니다.
  • 특징: 작은 메모리 공간으로, 다음 실행할 명령어를 찾는 데 사용됩니다. 예외 처리나 스레드 복원에도 쓰입니다.
  • 멀티스레딩: 여러 스레드가 번갈아 실행되므로, 각 스레드는 자신만의 PC 레지스터를 가집니다.
  • 예외: 자바가 아닌 다른 언어로 작성된 코드(네이티브 메서드)를 실행할 때는 값이 정의되지 않습니다.

2) 자바 스택

  • 역할: 메서드를 실행할 때 사용하는 공간입니다.
  • 구성: 메서드가 호출될 때마다 스택 프레임이 쌓입니다. 스택 프레임에는 아래 정보가 들어갑니다:
    • 지역 변수: 메서드에서 사용하는 기본 데이터(숫자, 문자 등)나 객체 참조.
    • 중간 계산값: 계산 중간 결과를 저장.
    • 연결 정보: 다른 메서드로 연결되는 데이터.
    • 반환 위치: 메서드 끝난 후 돌아갈 곳.
  • 특징: 컴파일할 때 크기가 정해지며, 각 스레드는 독립적인 스택을 가집니다. 보통 '스택'이라고 하면 이 지역 변수 저장소를 뜻합니다.
  • 데이터 크기: 기본 데이터는 32비트 슬롯 하나, 큰 데이터(long, double)는 두 슬롯을 사용합니다.

3) 네이티브 스택

  • 역할: 자바가 아닌 C나 C++로 작성된 네이티브 메서드를 실행할 때 사용됩니다.
  • 특징: 자바 스택과 비슷하지만, 네이티브 메서드 전용입니다. 각 스레드마다 따로 가집니다.

2. 모든 스레드가 함께 사용하는 메모리 영역

모든 스레드가 공유하는 메모리는 프로그램 전체에서 공통으로 필요한 데이터를 저장합니다.

1) 메서드 영역 (메타스페이스)

  • 역할: 클래스 정보, 정적 변수, 상수, 메서드 코드를 저장합니다.
  • 구성: 런타임 상수 풀이라는 공간이 포함되어 클래스에 정의된 상수를 관리합니다.
  • 특징: 모든 스레드가 공유합니다. JDK 8부터는 메타스페이스라는 이름으로 불립니다(예전에는 영구 세대라고 했습니다).

2) 힙

  • 역할: new로 만든 객체와 배열을 저장합니다.
  • 특징: 모든 스레드가 공유하며, 메모리가 부족하면 가비지 컬렉터(GC)가 필요 없는 객체를 지워 공간을 확보합니다.
  • 내부 구조: 효율적인 관리 위해 신세대구세대로 나뉩니다.
  • 객체 구성:
    • 헤더: 객체의 기본 정보(클래스 정보 등).
    • 데이터: 객체의 실제 값(필드).
    • 패딩: 메모리 정렬을 위한 여백.
  • 접근 방식: 핸들 방식(간접 참조) 또는 직접 포인터 방식으로 객체에 접근합니다. JVM마다 다릅니다.

메모리 문제 해결하기

JVM 메모리 영역을 이해하면 문제를 빠르게 해결할 수 있습니다. 예를 들어:

  • 스택 오버플로우: 스레드가 너무 많거나 스택 공간을 너무 많이 쓰면 자바 스택에서 문제가 생깁니다.
  • 힙 부족: 객체가 너무 많거나 가비지 컬렉션이 제때 안 되면 힙 메모리가 모자랍니다.
  • 메타스페이스 문제: 클래스를 너무 많이 로드하면 메서드 영역에서 오류가 날 수 있습니다.
반응형