-
Java off-heap 메모리 알아보기Language/Java 2023. 12. 22. 19:01728x90반응형
- 목차
소개.
Java 로 작성된 프로그램은 JVM 이라는 가상 머신 위에서 동작합니다.
OpenJDK, Correto, Zulu 등에 해당하는 여러 JDK 는 JVM 이라는 Java 가상 머신을 생성하게 되는데요.
Java 프로그램이 실행될 때마다 JVM 은 생성되고,
JVM 이 전적으로 Java 프로그램을 매니징하게 됩니다.
ByteCode 를 실행하고 실행에 필요한 메모리를 관리하는데 Heap, Stack, Class 등으로 표현되는 여러 메모리를 관리합니다.
그리고 이러한 메모리들은 Garbage Collector 의 모니터링 대상이 되구요.
Java 프로그램에서 생성하는 여러 객체들은 Garbar Collector 의 대상이 되므로
프로그래머의 관리 대상에서 제외되는 편리함이 있습니다.
오늘 소개할 off-heap memory 는 JVM 의 바깥에 위치하는 메모리로써
운영체제로부터 할당받는 프로세스의 메모리 영역입니다.
이번 글에서는 off-heap memory 가 무엇인지 알아보고 관련한 여러 예시들을 살펴볼 예정입니다.
Off-Heap Memory.
먼저 JVM 의 메모리 구조에 대한 이미지 하나를 공유하려고 합니다.
아래 이미지는 Apache Flink 라는 스트림 프로세싱 프레임워크와 관련된 메모리 구조 이미지입니다.
https://nightlies.apache.org/flink/flink-docs-master/docs/deployment/memory/mem_setup_jobmanager/
Java 프로그램이 실행되어 Java 프로세스가 되면 이는 크게 Process 와 JVM 영역으로 나눌 수 있습니다.
그러니깐 OS 관점의 Process 와 그 내부에 JVM 이 위치하게 됩니다.
JVM 은 하나의 가상 머신으로써 Java Byte Code 를 실행하게 되는데, JVM 은 그 내부에 메모리 영역을 추가적으로 두게 됩니다.
마치 VMWare 같은 가상 머신이 가상의 하드웨어를 만드는 것처럼 JVM 도 ByteCode 의 실행을 위한 메모리 영역을 구축합니다.
그렇다보니 Process 가 가지는 메모리 영역과 JVM 이 가지는 메모리 영역이 공존하게 됩니다.
( 사실 JVM 의 메모리 영역은 Process 의 메모리 영역 내부에 저장되긴 하지만. )
그리고 Java 프로그램은 딱 JVM 의 메모리 영역만을 사용하도록 제한됩니다.
Off-Heap Memory 는 이러한 JVM 메모리 영역 외의 Process 의 메모리 영역을 뜻합니다.
그리고 Off-Heap Memory 를 사용한다는 의미는 Process 의 메모리를 사용한다는 뜻이 됩니다.
Object object = new Object(); 와 같은 일반적인 방식으로 Off-Heap 메모리를 사용할 수는 없습니다.
그래서 Java 에서는 java.nio.ByteBuffer 라는 클래스를 활용하여 Off-Heap Memory 를 사용하게 됩니다.
Runtime memory.
아래 코드는 Java 프로그램에서 현재 사용하는 JVM 메모리 상태를 파악하는 간단한 코드 예시입니다.
public class TestByteBuffer { public static void main (String[] args) { Runtime runtime = Runtime.getRuntime(); System.out.println("maxMemory is " + runtime.maxMemory() / 1024 / 1024 + "mb"); System.out.println("freeMemory is " + runtime.freeMemory() / 1024 / 1024 + "mb"); System.out.println("totalMemory is " + runtime.totalMemory() / 1024 / 1024 + "mb"); } }
javac TestByteBuffer.java java TestByteBuffer
maxMemory is 8192mb freeMemory is 509mb totalMemory is 512mb
위 코드를 실행하게 되면 위처럼 maxMemory, freeMemory, totalMemory 를 알 수 있습니다.
totalMemory 는 현재 Java 프로그램이 사용할 수 있는 총 메모리 용량입니다.
freeMemory 는 현재 남아있는 여유분의 메모리 용량이구요.
maxMemory 는 Java 프로그램이 사용할 수 있는 메모리 제한입니다.
몇 가지 예시를 더 들어보겠습니다.
메모리를 계속 사용한다면 어떻게 될까 ?
List 자료구조에 100000000 개의 Object 객체를 생성 후 할당합니다.
List 자료구조에 의해서 Object 객체들은 Referencing 되므로 GC 되지 않습니다.
따라서 메모리 사용량이 증가하겠죠 ?
import java.util.ArrayList; import java.util.List; public class TestByteBuffer { public static void main (String[] args) { Runtime runtime = Runtime.getRuntime(); List<Object> list = new ArrayList<>(); System.out.println("initial maxMemory is " + runtime.maxMemory() / 1024 / 1024 + "mb"); System.out.println("initial freeMemory is " + runtime.freeMemory() / 1024 / 1024 + "mb"); System.out.println("initial totalMemory is " + runtime.totalMemory() / 1024 / 1024 + "mb"); for (long i = 0; i < 10000000; i++) { list.add(new Object()); } System.out.println("final maxMemory is " + runtime.maxMemory() / 1024 / 1024 + "mb"); System.out.println("final freeMemory is " + runtime.freeMemory() / 1024 / 1024 + "mb"); System.out.println("final totalMemory is " + runtime.totalMemory() / 1024 / 1024 + "mb"); } }
initial maxMemory is 8192mb initial freeMemory is 509mb initial totalMemory is 512mb final maxMemory is 8192mb final freeMemory is 3010mb final totalMemory is 5798mb
위 결과처럼 totalMemory 는 512mb 에서 5798mb 로 늘어나게 됩니다.
그러다가 maxMemory 를 초과하게 되면 OutOfMemory 상태가 됩니다.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.base/java.util.Arrays.copyOf(Arrays.java:3720) at java.base/java.util.Arrays.copyOf(Arrays.java:3689) at java.base/java.util.ArrayList.grow(ArrayList.java:238) at java.base/java.util.ArrayList.grow(ArrayList.java:243) at java.base/java.util.ArrayList.add(ArrayList.java:486) at java.base/java.util.ArrayList.add(ArrayList.java:499) at TestByteBuffer.main(TestByteBuffer.java:15)
MaxMemory 수정하기.
아래와 같은 방식으로 maxMemory 값을 수정할 수 있습니다.
기본 Max Memory 가 8gb 인 8192mb 상태인데요.
아래처럼 128mb 로 변경하게 되면 약간의 메모리 할당으로도 프로그램이 종료될 겁니다.
javac TestByteBuffer.java java -Xmx128m TestByteBuffer
java.nio.ByteBuffer
java.nio.ByteBuffer 는 Java 프로그램의 객체들을 Off-Heap Memory 로 저장할 수 있도록 하는 Java Package & Class 입니다.
java.nio 패키지의 ByteBuffer 클래스를 통해서 Off-Heap Memory 즉 Process 의 메모리에 접근할 수 있게 됩니다.
아래는 관련된 예시 코드 입니다.
import java.nio.ByteBuffer; public class TestByteBuffer { public static void main (String[] args) { Runtime runtime = Runtime.getRuntime(); System.out.println("initial maxMemory is " + runtime.maxMemory() / 1024 / 1024 + "mb"); System.out.println("initial freeMemory is " + runtime.freeMemory() / 1024 / 1024 + "mb"); System.out.println("initial totalMemory is " + runtime.totalMemory() / 1024 / 1024 + "mb"); ByteBuffer byteBuffer = ByteBuffer.allocate(8 * 100000000); for (long i = 0; i < 100000000; i++) { byteBuffer.putLong(new Long(i)); } System.out.println("final maxMemory is " + runtime.maxMemory() / 1024 / 1024 + "mb"); System.out.println("final freeMemory is " + runtime.freeMemory() / 1024 / 1024 + "mb"); System.out.println("final totalMemory is " + runtime.totalMemory() / 1024 / 1024 + "mb"); byteBuffer.flip(); System.out.println("first value is " + byteBuffer.getLong()); System.out.println("last value is " + byteBuffer.getLong(8 * 100000000 - 8)); } }
initial maxMemory is 8192mb initial freeMemory is 509mb initial totalMemory is 512mb final maxMemory is 8192mb final freeMemory is 492mb final totalMemory is 1276mb first value is 0 last value is 99999999
10000000개의 Long 객체를 생성하지만, JVM 의 메모리에는 큰 변경이 없습니다.
10000000개의 Long 객체들은 Serialized 되어 Off-Heap Memory 에 저장되기 때문입니다.
이러한 방식으로 JVM 의 메모리 사용량을 줄일 수 있습니다.
반응형'Language > Java' 카테고리의 다른 글
[Java NIO] NIO 는 어떻게 동작할까 ? (DMA, Kernel Space) (0) 2024.01.31 Jackson 으로 JSON 다루기 (0) 2024.01.22 Java Thread Pool 알아보기 (0) 2023.09.30 Java Future 알아보기 (0) 2023.09.30 java Annotation 이해하기 (0) 2023.09.25