JVM Memory

Andy (Yoon Yong) Shin·2022년 1월 2일
0

개요

Flink나 Kafka Streams 같이 실시간으로 데이터 처리를 해주는 기술로 사용되는 framework들은 JVM에서 실행되기 때문에 Garbage Collection (GC) 에 예민 할수 밖에 없습니다. 그 이유는 GC 가 일어나는 동안에는 모든 프로그램이 멈추기 때문이며, 이는 실시간 처리에 문제가 됩니다. 이를 통상적으로 stop the world event라 칭하며, Java version이 업그레이드 되면서 Garbage Collector가 많이 똑똑해지며, GC를 최소화 하고, Minor GC로서 해결하게 많들어 주지만, 사실 Minor이던 Major이던 GC가 stop the world event (STW) 라는 것은 변함이 없습니다. 이를 해결하기 위해 Flink나 Kafka Streams 같은 경우 Java GC에서 벗어난 Off-Heap 이라는 메모리 공간을 활용하여, 해당 문제를 최소화 시킵니다. 이번 글은 Java process에 할당 되는 메모리들이 어떻게 분할되어 사용 되는지 대해서 알아 보겠습니다.

출처: https://nightlies.apache.org/flink/flink-docs-release-1.14/docs/deployment/memory/mem_setup_tm/

Java에 할당되는 메모리는 아래와 같이 요약할 수 있을거 같습니다.

Process Memory

Process memory는 java process에 총 할당된 memory 이며, 이 process memory는 JVM heap memory와 off-heap / native memory로 나누어 집니다.

JVM Heap Memory

Garbage Collector에 의해 관리되는 메모리 영역이며, java 프로그램에서 objects들을 저장하고 관리하기 위한 공간입니다. Java는 GC라는 기능 때문에 개발적으로 많이 편리해졌지만, 이와 함께 특정 목적으로 개발할을 memory를 낭비없이 관리해야 하는 상황에는 STW 같은 단점이 발생합니다.

Off Heap / Native Memory

JVM heap memory와 동일한 process memory 속해있지만, Garbage Collector에 의해 GC 되지 않는 memory 입니다.. Java에서 Java Native Interface (JNI) 라는 기술로 low level language에 의존하는 형식으로 code로서는 아래와 같이 native keyword로 interface만 볼수 있습니다. 실제로는 구현체가 JDK에 숨겨져있으며, 내부에는 C 코드로 구현 되어 melloc calloc free으로 메모리 관리가 되고 있습니다. 단점으로는 heap과 반대로 덤프나 jstat 같은 java 일반적인 메모리 분석 방법이 안되어, jcmd와 같은 추가적인 도구를 사용해서 확인해야 합니다.

JDK java.util.zip package

private static native void initIDs();
private native static long init(int level, int strategy, boolean nowrap);
private native static void setDictionary(long addr, byte[] b, int off, int len);
private native int deflateBytes(long addr, byte[] b, int off, int len,
                                int flush);
private native static int getAdler(long addr);
private native static void reset(long addr);
private native static void end(long addr);

Direct Memory

Native memory에 포함되어 있는 memory 이지만, OS에 제공하는 pagecache 기능을 direct하게 사용할 수 있는 공간입니다. 초기에 Java는 IO operation들이 (java.io package) 다른 low level language보다 느렷던 이유는 Java 이념인 write once run anywhere를 지키기위해 OS별로 system call을 구현하기 보다 data를 불러 들여올때 여러단계의 data를 copy 하는 형식을 취해 그런 것이며, 이를 보안 하기 위해 nio package를 구현하여, zero-copy의 개념으로 데이터를 io를 통해 불러올 수 있게 됐습니다. (자세한 내용은 여기서 그리고 여기서 확인 가능 합니다) Flink나 kafka는 대용량의 데이터를 cluster로 처리 하기 때문에, IO overhead를 최소화 하기 위해 IO는 direct memory로 최대한 처리 합니다.

JVM Metaspace

JVM Mataspace는 JVM heap memory에 포함 되어 있지 않으며, 프로그램에 따라 memory size가 천차 만별입니다. 해당 memory 공간은 주로 Java class에 대한 metadata가 저장됩니다.

JVM Overhead

JVM Overhead는 JVM heap memory에 포함 되어 있지 않으며, 위에 나열하지 않은 다른 overhead를 관리하는데 사용되는 memory 입니다. 예를 들어 thread stacks, code cache, garbage collection space 같은 것들이 존재 합니다.

끝으로

실시간 처리기능을 문제 없이 제공하기 위해 Java 메모리 관리법을 알아 봤습니다. Flink와 같은 stream processing framework가 Golang같은 language로도 나왔으면 좋습니다... (메모리 사용이 어찌하다 이리 복잡해 졌을까요...) 적고보니 생각보다 분량이 되지않아 추후 Flink에서는 어떤 형식으로 direct memory를 사용하는 지 알아보는 세션도 준비해볼거 같습니다.

참조

https://nightlies.apache.org/flink/flink-docs-release-1.14/docs/deployment/memory/mem_setup_tm/

https://homoefficio.github.io/2020/08/10/Java-NIO-FileChannel-과-DirectByteBuffer/

https://homoefficio.github.io/2016/08/06/Java-NIO는-생각만큼-non-blocking-하지-않다/

http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/

https://medium.com/@tas.com/파일-시스템에-대한-기초적인-개념정리-9144dabce95d

https://brunch.co.kr/@alden/25

https://developer.ibm.com/articles/j-zerocopy/

https://docs.confluent.io/platform/current/kafka/design.html#offline-data-load

https://stuefe.de/posts/metaspace/what-is-metaspace/

https://www.oracle.com/technetwork/tutorials/tutorials-1873457.html

https://medium.com/swlh/native-memory-the-silent-jvm-killer-595913cba8e7#:~:text=Native memory is memory allocated,methods in certain Java classes.&text=When you get a heap,heap%2C as the name implies.

0개의 댓글