Java 애플리케이션의 성능을 개선하고 관리하는 것은 중요하며, JVM(Java Virtual Machine) 메모리 구조와 관리에 대한 이해는 이러한 과정에서 핵심적인 역할을 합니다. 이 확장된 가이드는 JVM의 메모리 영역의 복잡성에 대해 깊이 파고들며, 효과적인 메모리 관리를 통해 Java 애플리케이션 성능을 향상시키는 방법에 대해 알아보겠습니다.

JVM의 메모리 영역 소개

JVM 메모리, 또는 런타임 데이터 영역은 애플리케이션 실행과 성능에 각각 독특한 목적을 제공하는 여러 주요 섹션으로 나뉩니다.

Method Area

Method Area는 JVM이 클래스와 인터페이스의 메타데이터를 저장하는 영역으로, 클래스 생성자 및 메소드의 바이트 코드 등이 여기에 위치합니다. 클래스의 인스턴스가 생성되고 메소드가 호출될 때, 해당 클래스의 정보가 Method Area에 저장됩니다. 이 영역은 모든 스레드에 의해 공유되며, JVM 시작 시 생성됩니다.

Method Area는 모든 Thread에 의해 공유되는 영역 이며, JVM이 시작될 때 생성된다.

Method Area 는 JVM 제품(벤더)에 따라 구현이 다르다. Hotspot JVM(Oracle)의 Method Area 는 Permanent Area(PermGen)이라고 한다.

Method Area - Runtime Constant Pool

Method Area 는 내부에 Runtime Constant Pool 영역을 가지고 있다. Runtime Constant Pool 영역에는 클래스/인터페이스의 메소드, 필드, 문자열 상수등의 레퍼런스 가 저장되며, 이들의 물리적인 메모리 위치를 참조할 경우에 사용된다.

Heap

Heap 영역은 new 연산자를 통해 생성된 객체와 배열을 저장하는 공간입니다. 이 영역에 저장된 객체들은 다른 객체에 의해 참조될 수 있으며, 참조되지 않는 객체들은 가비지 컬렉션(GC) 과정을 통해 메모리에서 제거됩니다. Heap 영역은 내부적으로 여러 섹션으로 나누어져 있으며, 이는 객체의 생명주기 및 GC와 밀접한 관련이 있습니다.

튜닝 옵션: -Xms는 초기 힙 사이즈, -Xmx는 최대 힙 사이즈를 설정합니다.

JVM Language Stacks

각 메소드 호출 시, 실행 중인 메소드의 데이터(지역 변수, 객체 참조, 파라미터, 리턴 값 등)를 저장하기 위한 영역입니다. 스택 영역은 각 스레드별로 독립적으로 생성되며, 메소드 호출 시 스택 프레임이 생성되어 데이터를 저장합니다. 메소드 실행이 완료되면 해당 스택 프레임은 제거됩니다.

PC Registers

PC(Program Counter) 레지스터는 각 스레드별로 할당되며, 스레드가 시작될 때 생성됩니다. 이 영역에는 스레드가 실행할 JVM 명령의 주소가 저장되며, JVM은 이 명령들을 순차적으로 실행하여 Java 애플리케이션을 실행합니다.

Native Method Area

Java 외의 언어로 작성된 코드를 실행하기 위한 스택 영역입니다. JNI(Java Native Interface)를 통해 실행되는 네이티브 코드를 위한 스택이 각 언어별로 생성됩니다.

JVM Memory 구조

Heap 영역 자세히 보기

Heap 영역은 객체와 배열이 저장되는 공간으로, 가비지 컬렉션(GC)의 주요 대상입니다. GC는 메모리의 효율적 사용을 위해 더 이상 필요하지 않은 객체를 메모리에서 제거하는 과정입니다.

튜닝 옵션

-Xms : 초기 heap size –Xmx : 최대 heap size

JVM Heap 구조

Eden 영역 (Young 영역)

새로 생성된 대부분의 객체가 처음 위치하는 영역을 의미한다. Eden 영역에서 정기적으로 GC가 발생한 이후, 살아남은 객체들은 Survivor1 또는 Survivor2 중 선택된 하나의 영역으로만 이동하여 계속 쌓이게 된다.

Survivor1, Survivor2 영역 (Young 영역)

Survivor1 또는 Survivor2 중 하나의 영역이 꽉 차게되면, 그 중 살아남은 객체가 비워진 Survivor 영역으로 이동한다. 이때 참조가 없는 객체들을 메모리에서 정리된다.

예를 들면, Survivor1 가 꽉찬 경우 살아남은 객체만 Survivor2 로 이동하게 되는 것이다.

이러한 매커니즘 때문에, Survivor1 또는 Survivor2는 항상 비워진 상태가 되며, Survivor 영역 중 하나의 영역이 완전히 비워지지 않았다면, 문제가 있는 것이다.

Minor GC

Eden 영역 또는 Survivor1, Survivor2 영역 등, Young 영역에서 발생하는 GC를 minor GC라고 한다.

튜닝 옵션

-XX:NewSize : 최소 new size (Eden+Survivor 영역) -XX:MaxNewSize : 최대 new size -XX:SurvivorRatio : New/Survivor영역 비율

Old 영역

Survivor1 또는 Survivor2 영역을 왔다갔다 하는 과정에서 끝까지 살아남은 객체만이 Old 영역으로 이동하게 된다. 보통 Old 영역은 Young 영역보다 크게 할당하며, 이러한 이유로 Old 영역의 GC는 Young 영역보다 적게 발생한다.

Major GC

Old 영역, Permanent 영역에서 발생하는 GC를 Major GC (Full GC)라 한다.

Permanent 영역

클래스 로더에 의해 로드된 클래스들이 저장되는 공간이다. Java8 에서는 Permanent 영역이 좀 더 다듬어져 Metaspace 라는 영역으로 교체되었다.

튜닝 옵션

-XX:PermSize : 초기 Perm size -XX:MaxPermSize : 최대 Perm size

JVM 메모리 영역내에서 객체의 Lifecycle

JVM 메모리 영역내에서 객체의 Lifecycle의 흐름을 보면 아래와 같다.

Java 8 의 Metaspace 영역

Java 8에서는 PermGen 영역의 문제를 해결하기 위해 Metaspace가 도입되었습니다. Metaspace는 클래스 메타데이터를 저장하는 영역으로, 네이티브 메모리를 사용합니다. 이 변경을 통해 Static Object와 상수화된 String Object가 Heap 영역으로 이동하여 GC의 대상이 될 수 있게 되었습니다.

튜닝 옵션

-XX:MetaspaceSize : JVM이 사용하는 네이티브 메모리 -XX:MaxMetaspaceSize : metaspace의 최대 메모리

결론

JVM의 메모리 구조와 관리는 Java 애플리케이션의 성능 최적화에 매우 중요합니다. 개발자는 이러한 메모리 구조를 정확히 이해하고, 애플리케이션의 요구 사항에 맞게 적절한 메모리 관리 및 튜닝 옵션을 적용해야 합니다. 이를 통해 애플리케이션의 성능을 향상 시키고 안정적으로 운영할 수 있습니다.

태그: , ,

카테고리:

업데이트:

댓글남기기