본문 바로가기
Java

Java 메모리 영역과 Overflow

by fygoo-826 2025. 1. 23.
728x90

 Java 애플리케이션이 실행될 때, Java Virtual Machine(JVM)은 여러 가지 데이터 영역을 관리한다. 이 글에서는 JVM의 런타임 데이터 영역에 대해 정리해보려고 한다.

 적어도 컴퓨터 구조나 JVM, 폰 노이만 구조 등 기초 용어들은 알고 있다는 가정하에 포스팅을 읽기를 바란다.


런타임 데이터 영역 정리

## jvm에서 program conter register

Program Counter(PC) 레지스터는 현재 실행 중인 Java 스레드의 다음 명령어의 주소를 저장한다. 각 스레드는 독립적인 PC 레지스터를 가지며, 스레드가 실행될 때마다 이 값을 업데이트한다. 예를 들어 아래 코드가 있다고 하면 jvm 에서는 main 메서드 정보를 program counter register 에서는 main 메서드 주소를 들고 있다.

public class CounterExample {
    public static void main(String[] args) { // 메서드 주소가 어딘가에 저장되어있을 것이다
        System.out.println("Hello, World!");
    }
}

 

## jvm 에서 자바 가상 머신 stack

ref. https://www.realjavaonline.com/java-virtual-machine/java-virtual-machine.php

자바 가상 머신 스택은 각 스레드마다 존재하며, 메서드 호출 시에 사용되는 프레임(모든 지역 변수, 운영 정보 등)을 관리한다. 메서드 호출이 있을 때마다 새로운 프레임이 생성되고, 메서드가 종료되면 해당 프레임은 스택에서 제거된다. 아래는 스택 프레임에 포함된 요소들이다.

 

ref. https://itnext.io/the-memory-structure-of-jvm-part-1-a9ce679f70ac

위 그림에서 Stack Frame 안에는 아래 정보들이 저장되어 있다. 참고로 기억해두자.

  • 지역 변수
  • 피연산자 스택
  • 메서드의 반환 주소
  • 공통 라이브러리 저장 영역

## jvm 에서 native method stack

 Native Method Stack은 JNI(Java Native Interface)를 사용하여 C 또는 C++ 언어로 작성된 메서드 호출 시 사용된다. 이 스택은 자바 가상 머신 스택과 유사하게, 네이티브 메서드 호출 정보를 저장한다. 네이티브 메서드는 자바와 직접 연결되기 때문에 잘 활용해야 한다.

 가끔 코드를 타고 들어가다보면 `native` 라는 키워드를 가진 메서드를 만나게 된다. 해당 메서드가 native method stack 에 실행 정보를 저장하고 있다고 이해하면 쉽다.

 

## jvm 에서 heap

Heap 메모리는 모든 객체의 인스턴스와 배열을 저장하는 곳이다. JVM이 시작될 때, Heap 메모리는 초기화되며 크기는 설정에 따라 달라질 수 있다. Heap 메모리에 생성된 객체는 가비지 컬렉션(Garbage Collection) 대상이 된다.

예를 들어, Heap에 생성된 객체는 다음과 같을 수 있다.

 

class Person { // heap 영역 저장 #1
    String name; // heap 영역 저장 #2

    Person(String name) {
        this.name = name;
    }
}

Person p = new Person("Garrett"); // #1 을 저장한다

 

## jvm 에서 method area

 Method Area는 클래스 정보, 메서드 코드, 필드 정보, static 메모리 등을 저장하는 공간이다. 모든 클래스에 대한 정보는 단 하나의 Method Area에 저장되며, JVM이 클래스 로딩 시에 메모리에 올려진다. 

 만일 static 코드를 자주 사용한다면 클래스가 로딩되는 순간 메모리 영역을 차지한다고 이해하면 된다.

 

## jvm 에서 runtime 상수 풀

 Runtime Constant Pool은 각 클래스에 대한 상수 및 리터럴 값들을 저장한다. 이는 Method Area의 일부로, 문자열 리터럴과 숫자 상수 등이 여기에 포함된다.

 세분화 하면 String 상수 풀, Constant Pool, Runtime Constant Pool 등이 세분화된다. 저장되는 정보들이 다른데 자세한 건 아래 링크를 참조하면 큰 도움이 된다. 

https://deveric.tistory.com/123

 

## Direct memory 란

 Direct Memory는 JVM의 Heap 외부에서 사용할 수 있는 메모리 영역이다. Java NIO(New Input/Output) API를 통해 직접 메모리를 할당 받을 수 있으며, 이는 성능 향상을 가져올 수 있지만, 가비지 컬렉션의 영향을 받지 않는다. 

 좀 더 상세하게는 jvm 에 의해서 관리되지 않는 메모리 영역이라고 생각하면 된다. 보통 커널 메모리를 할당받아 사용한다. 이 부분을 이해하기 위해서는 사실 컴퓨터에 프로세스에 대한 이해가 선행되면 좋다. 이 부분은 나중에 좀 더 자세히 풀어내보려고 한다. 현재까지는 jvm 에서 관리되지않는 커널 메모리를 의미한다고 이해해도 충분한다.

 


Hotspot 가상 머신에서 객체 

## 객체 생성

Java에서 객체는 new 키워드를 사용하여 생성된다. 객체가 생성될 때 JVM은 Heap 메모리에서 메모리를 할당하고 해당 객체의 초기화를 한다.

Human 구피 = new Human();


## 객체의 memory layout

객체는 일련의 필드로 구성된다. JVM은 각 필드를 위해 메모리를 할당하며, 객체의 메모리 레이아웃은 다음과 같다.

헤더 : 객체의 메타데이터가 포함됨
인스턴스 필드 : 객체의 상태를 나타내는 데이터
예를 들어, 객체의 메모리 레이아웃은 다음과 같을 수 있다.

+---------------------+
| 헤더                |
+---------------------+
| 인스턴스 필드 1    |
+---------------------+
| 인스턴스 필드 2    |
+---------------------+

 

 


 

OutofMemoryError 란?

 

OutOfMemoryError는 JVM이 메모리를 더 이상 할당할 수 없을 때 발생한다. 아래는 여러 상황 별 발생 원인이다.

## JVM Stack과 Native Method 2가지 영역에서 StackOverflow
메서드 호출이 너무 깊이 중첩되면 StackOverflowError가 발생할 수 있으며, 이는 JVM 스택과 네이티브 메서드 스택 모두에 적용된다.

## JVM Method 영역과 Runtime 상수 풀 2가지 영역에서 StackOverflow
이 두 영역에 대한 오버플로우는 주로 작업 과다로 인해 발생한다. 많은 클래스와 메서드가 생성될 경우, 메모리가 고갈되기 쉽다.

## Direct Memory에서 Overflow
Direct Memory 또한 한정된 공간이기 때문에, 너무 많은 메모리를 할당받거나 해제하지 않으면 OutOfMemoryError가 발생할 수 있다. NIO를 사용할 때 주의할 부분이다.

이번 포스팅에서 JVM 런타임 데이터 영역에 대한 기본 개념을 정리하였다. 앞으로 소개할 많은 포스팅에서 도움이 되는 기초 지식만 간단하게 기록해보았다. Java 개발자로서 이 영역의 이해는 성능 및 메모리 관리에 큰 도움이 될 거라 확신한다. 

728x90