Java가 히프 크기(또는 도커 메모리 제한 크기)보다 훨씬 많은 메모리를 사용합니다.
내 어플리케이션의 경우 Java 프로세스에서 사용되는 메모리가 힙 크기보다 훨씬 큽니다.
컨테이너가 힙 크기보다 훨씬 더 많은 메모리를 사용하기 때문에 컨테이너가 실행 중인 시스템에 메모리 문제가 발생하기 시작합니다.
는 128 128 MB')로.-Xmx128m -Xms128m
gbgb 1GB 。500MB를 사용하다도커 컨테이너에 다음과 같은 제한이 있는 경우(예:mem_limit=mem_limit=400MB
Java 프로세스가 힙보다 훨씬 더 많은 메모리를 사용하는 이유를 설명해 주시겠습니까?도커 메모리 제한을 올바르게 사이징하는 방법Java 프로세스의 메모리 용량을 줄일 수 있는 방법이 있습니까?
JVM의 Native Memory Tracking 명령어를 사용하여 문제에 대한 자세한 내용을 수집합니다.
호스트 시스템에서 컨테이너에서 사용되는 메모리를 가져옵니다.
$ docker stats --no-stream 9afcb62a26c8
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
9afcb62a26c8 xx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.0acbb46bb6fe3ae1b1c99aff3a6073bb7b7ecf85 0.93% 461MiB / 9.744GiB 4.62% 286MB / 7.92MB 157MB / 2.66GB 57
컨테이너 안에서 프로세스에서 사용되는 메모리를 가져옵니다.
$ ps -p 71 -o pcpu,rss,size,vsize
%CPU RSS SIZE VSZ
11.2 486040 580860 3814600
$ jcmd 71 VM.native_memory
71:
Native Memory Tracking:
Total: reserved=1631932KB, committed=367400KB
- Java Heap (reserved=131072KB, committed=131072KB)
(mmap: reserved=131072KB, committed=131072KB)
- Class (reserved=1120142KB, committed=79830KB)
(classes #15267)
( instance classes #14230, array classes #1037)
(malloc=1934KB #32977)
(mmap: reserved=1118208KB, committed=77896KB)
( Metadata: )
( reserved=69632KB, committed=68272KB)
( used=66725KB)
( free=1547KB)
( waste=0KB =0.00%)
( Class space:)
( reserved=1048576KB, committed=9624KB)
( used=8939KB)
( free=685KB)
( waste=0KB =0.00%)
- Thread (reserved=24786KB, committed=5294KB)
(thread #56)
(stack: reserved=24500KB, committed=5008KB)
(malloc=198KB #293)
(arena=88KB #110)
- Code (reserved=250635KB, committed=45907KB)
(malloc=2947KB #13459)
(mmap: reserved=247688KB, committed=42960KB)
- GC (reserved=48091KB, committed=48091KB)
(malloc=10439KB #18634)
(mmap: reserved=37652KB, committed=37652KB)
- Compiler (reserved=358KB, committed=358KB)
(malloc=249KB #1450)
(arena=109KB #5)
- Internal (reserved=1165KB, committed=1165KB)
(malloc=1125KB #3363)
(mmap: reserved=40KB, committed=40KB)
- Other (reserved=16696KB, committed=16696KB)
(malloc=16696KB #35)
- Symbol (reserved=15277KB, committed=15277KB)
(malloc=13543KB #180850)
(arena=1734KB #1)
- Native Memory Tracking (reserved=4436KB, committed=4436KB)
(malloc=378KB #5359)
(tracking overhead=4058KB)
- Shared class space (reserved=17144KB, committed=17144KB)
(mmap: reserved=17144KB, committed=17144KB)
- Arena Chunk (reserved=1850KB, committed=1850KB)
(malloc=1850KB)
- Logging (reserved=4KB, committed=4KB)
(malloc=4KB #179)
- Arguments (reserved=19KB, committed=19KB)
(malloc=19KB #512)
- Module (reserved=258KB, committed=258KB)
(malloc=258KB #2356)
$ cat /proc/71/smaps | grep Rss | cut -d: -f2 | tr -d " " | cut -f1 -dk | sort -n | awk '{ sum += $1 } END { print sum }'
491080
이 애플리케이션은 36MB의 대용량 내부에 번들된 Jetty/Jersey/CDI를 사용하는 웹 서버입니다.
가음 、 OS 、 Java 、 컨( 、 (((( ( (테((((((컨도커 이미지의 베이스는 다음과 같습니다.openjdk:11-jre-slim
.
$ java -version
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment (build 11+28-Debian-1)
OpenJDK 64-Bit Server VM (build 11+28-Debian-1, mixed mode, sharing)
$ uname -a
Linux service1 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 GNU/Linux
https://gist.github.com/prasanthj/48e7063cac88eb396bc9961fb3149b58
Java 프로세스에서 사용되는 가상 메모리는 Java 힙을 훨씬 능가합니다.JVM에는 가비지 콜렉터, 클래스 로딩, JIT 컴파일러 등 많은 서브시스템이 포함되어 있습니다.이러한 서브시스템이 기능하려면 일정량의 RAM이 필요합니다.
JVM만이 RAM을 사용하는 것은 아닙니다.네이티브 라이브러리(표준 Java 클래스 라이브러리 포함)도 네이티브 메모리를 할당할 수 있습니다.네이티브 메모리 트래킹에서는 볼 수 없습니다.Java 애플리케이션 자체는 직접 바이트 버퍼를 통해 오프히프 메모리를 사용할 수도 있습니다.
Java 프로세스에서 메모리가 필요한 것은 무엇일까요?
JVM 부품(대부분 네이티브 메모리 트래킹으로 표시)
- 자바 힙
가장 확실한 부분.Java ★★★★★★★★★★★★★★★★★★★★★★★★★★★」 " " 사용"-Xmx
메모리 용량
- 가비지 콜렉터
GC 구조 및 알고리즘은 힙 관리를 위해 추가 메모리가 필요합니다.마크 비트맵, 마크 스택, 기억 세트(기억 세트).그 중 일부는 직접 조정할 수 있습니다.-XX:MarkStackSizeMax
를 들어 큰 지역G1 지역)입니다.G1은 G1에 있다(G1은 G1에 있다.-XX:G1HeapRegionSize
작은 것은 기억되는 세트입니다.
GC 메모리의 오버헤드는 GC 알고리즘에 따라 다릅니다. -XX:+UseSerialGC
★★★★★★★★★★★★★★★★★」-XX:+UseShenandoahGC
는 히프의 약 10%를 쉽게 할 수 .G1 또는 CMS는 전체 힙 크기의 약 10%를 쉽게 사용할 수 있습니다.
- 코드 캐시
동적으로 생성된 코드(JIT 컴파일된 메서드, 인터프리터 및 런타임스텁)가 포함됩니다.그 크기는 다음과 같이 제한됩니다.-XX:ReservedCodeCacheSize
(2월 2일)-XX:-TieredCompilation
컴파일된 코드의 양을 줄여 코드 캐시 사용량을 줄입니다.
- 컴파일러
JIT 컴파일러 자체도 작업을 수행하기 위해 메모리가 필요합니다.스레드 수 .-XX:CICompilerCount
.
- 클래스 로드
클래스 메타데이터(메서드 바이트 코드, 기호, 상수 풀, 주석 등)는 메타스페이스라고 하는 오프히프 영역에 저장됩니다.더 많은 클래스가 로드될수록 더 많은 메타스페이스가 사용됩니다.은 사용량에 따라 될 수 있습니다.-XX:MaxMetaspaceSize
및 (디폴트)-XX:CompressedClassSpaceSize
(1G).
- 기호 테이블
JVM의 두 가지 주요 해시 테이블: 기호 테이블에는 이름, 서명, 식별자 등이 포함되어 있으며 문자열 테이블에는 내부 문자열에 대한 참조가 포함되어 있습니다.테이블에 이 현저한 것을 , 이는 어플리케이션이 Native Memory Tracking String을 하게 호출하고 을 의미할 수 .String.intern
.
- 스레드
램는, 「스택 사이즈」에 의해서 됩니다.-Xss
기본값은 스레드당 100M이지만 다행히 상황은 나쁘지 않습니다.OS는 메모리 페이지를 느슨하게 할당하기 때문에(즉, 처음 사용했을 때) 실제 메모리 사용량은 훨씬 낮아집니다(일반적으로 스레드 스택당 80~200KB).자바 스레드 스택에 속하는 RSS의 양을 추정하기 위해 스크립트를 작성했습니다.
네이티브 메모리를 할당하는 다른 JVM 부품도 있지만 일반적으로 전체 메모리 사용량에서 큰 역할을 하지 않습니다.
직접 버퍼
으로 요구할 수 메모리를 요구하려면 , 「」를 호출합니다.ByteBuffer.allocateDirect
. 오프히프 은 "" 입니다-Xmx
, 이 ,, 이, 이, 이, 이 으로 덮어쓸 수 있습니다.-XX:MaxDirectMemorySize
. Byte 는 . Direct Byte Buffer에 되어 있습니다.Other
NMT 출력 섹션)Internal
(JDK 11 전))
사용된 직접 메모리의 양은 JMX를 통해 확인할 수 있습니다. 예를 들어 JConsole 또는 Java Mission Control:
ByteBuffer 에도 'ByteBuffer'를 할 수 .MappedByteBuffers
.- 할 수 .NMT는 Mapped Byte Buffer를 Mapped Byte Buffer로 지정합니다.그리고 그들이 복용할 수 있는 양을 제한할 수 있는 간단한 방법은 없다.맵을 을 알 수.pmap -x <pid>
Address Kbytes RSS Dirty Mode Mapping
...
00007f2b3e557000 39592 32956 0 r--s- some-file-17405-Index.db
00007f2b40c01000 39600 33092 0 r--s- some-file-17404-Index.db
^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
네이티브 라이브러리
JNI에 의해 System.loadLibrary
는 JVM 하지 않고 할 수 .자바 클래스 라이브러리특히 닫히지 않은 Java 리소스가 네이티브 메모리 누수의 원인이 될 수 있습니다.대표적인 예는 다음과 같습니다.ZipInputStream
★★★★★★★★★★★★★★★★★」DirectoryStream
.
JVMTI 에이전트jdwp
agent - 도 있습니다.
이 답변에서는 비동기 프로파일러를 사용하여 네이티브메모리 할당을 프로파일 하는 방법에 대해 설명합니다.
할당자 문제
에 의해).mmap
콜) 「」를 사용해 .malloc
libc - "libc allocator."국 in.malloc
는 OS에서 합니다.mmap
그런 다음 자체 할당 알고리즘에 따라 이러한 청크를 관리합니다.문제는 이 알고리즘으로 인해 단편화 및 과도한 가상 메모리 사용이 발생할 수 있다는 것입니다.
대체 할당자인 는 보통 libc보다 더 똑똑하게 나타납니다jemalloc
.malloc
로 전환합니다.jemalloc
을 사용하다
결론
고려해야 할 요소가 너무 많기 때문에 Java 프로세스의 전체 메모리 사용량을 추정할 수 있는 확실한 방법은 없습니다.
Total memory = Heap + Code Cache + Metaspace + Symbol tables +
Other JVM structures + Thread stacks +
Direct buffers + Mapped files +
Native Libraries + Malloc overhead + ...
JVM 플래그를 사용하여 특정 메모리 영역(예: 코드 캐시)을 축소하거나 제한할 수 있지만, 다른 많은 영역은 JVM 제어에서 전혀 벗어나 있습니다.
도커 제한을 설정하는 방법 중 하나는 프로세스의 "통상적인" 상태에서 실제 메모리 사용량을 확인하는 것입니다.Java 메모리 소비 문제를 조사하기 위한 도구와 기술은 다음과 같습니다.Native Memory Tracking, pmap, jemalloc, async-profiler.
갱신하다
다음은 Java 프로세스의 메모리 풋프린트 프레젠테이션 기록입니다.
이 비디오에서는 Java 프로세스에서 메모리를 소비할 수 있는 것, 특정 메모리 영역의 크기를 감시 및 제한하는 방법, Java 애플리케이션에서의 네이티브 메모리 누전 프로파일링 방법에 대해 설명합니다.
https://developers.redhat.com/blog/2017/04/04/openjdk-and-containers/:
-Xmx=1g을 지정하면 JVM이 메모리 1GB보다 더 많은 메모리를 사용하는 이유는 무엇입니까?
-Xmx=1g을 지정하면 JVM에 1GB 힙을 할당하도록 지시됩니다.JVM에 전체 메모리 사용량을 1GB로 제한하라고 지시하지는 않습니다.카드 테이블, 코드 캐시 및 기타 모든 종류의 오프히프 데이터 구조가 있습니다.총 메모리 사용량을 지정하는 데 사용하는 매개 변수는 -XX:MaxRAM입니다.-XX:MaxRam=500m의 경우 힙은 약 250MB가 됩니다.
Java는 호스트 메모리 크기를 인식하며 컨테이너 메모리 제한을 인식하지 않습니다.메모리 압력이 발생하지 않기 때문에 GC는 사용후 메모리를 해방할 필요도 없습니다.그러길 바랍니다.XX:MaxRAM
메모리 설치 공간을 줄이는 데 도움이 됩니다.GC 설정을 할 수 (GC 설정-XX:MinHeapFreeRatio
,-XX:MaxHeapFreeRatio
) , ... )
메모리 메트릭에는 여러 가지 유형이 있습니다.것 같습니다.는 RSS 메모리사이즈가 하는 「 수 .이것은, 에 의해서 보고되는 「커밋」메모리와는 다를 수 있습니다.jcmd
(이전 버전의 Docker는 RSS+캐시를 메모리 사용량으로 보고합니다).좋은 설명과 링크: 도커 컨테이너에서 실행되는 JVM의 상주 세트 크기(RSS)와 Java 총 인정 메모리(NMT)의 차이
(RSS) 메모리는 셸, 프로세스 매니저 등 컨테이너 내의 다른 유틸리티에도 사용할 수 있습니다.컨테이너에서 다른 무엇이 실행되고 있는지, 컨테이너에서 프로세스를 어떻게 시작하는지 알 수 없습니다.
TL;DR
메모리의 상세한 사용법은 Native Memory Tracking(NMT; 네이티브메모리 트래킹)의 상세(주로 코드 메타데이터 및 가비지 콜렉터)에 의해 제공됩니다.또한 Java 컴파일러 및 옵티마이저 C1/C2는 요약에 보고되지 않은 메모리를 소비합니다.
메모리 설치 공간은 JVM 플래그를 사용하여 줄일 수 있지만 영향이 있습니다.
도커 컨테이너 사이징은 예상되는 애플리케이션 로드 테스트를 통해 수행해야 합니다.
각 컴포넌트 상세
클래스는 다른 JVM 프로세스에서 공유되지 않으므로 컨테이너 내에서 공유 클래스 공간을 사용하지 않도록 설정할 수 있습니다.다음 플래그를 사용할 수 있습니다.공유 클래스 공간(17MB)이 제거됩니다.
-Xshare:off
가비지 콜렉터의 시리얼은, 가비지 콜렉터의 처리중의 포즈 시간이 길어지는 한편으로 최소한의 메모리 풋 프린트를 가지고 있습니다(한 장의 GC와의 Aleksey Shipilév 비교 참조).다음의 플래그를 사용해 유효하게 할 수 있습니다.사용된 GC 공간(48MB)까지 절약할 수 있습니다.
-XX:+UseSerialGC
C2 컴파일러는 다음 플래그로 비활성화하여 메서드의 최적화 여부를 결정하기 위해 사용되는 프로파일링 데이터를 줄일 수 있습니다.
-XX:+TieredCompilation -XX:TieredStopAtLevel=1
코드 공간은 20MB 줄어들고 JVM 외부 메모리는 80MB(NMT 공간과 RSS 공간의 차이) 감소합니다.최적화 컴파일러 C2에는 100MB가 필요합니다.
C1 및 C2 컴파일러는 다음 플래그를 사용하여 비활성화 시킬 수 있습니다.
-Xint
이제 JVM 외부의 메모리가 커밋된 총 공간보다 적습니다.코드 공간이 43MB 감소합니다. 이는 애플리케이션 성능에 큰 영향을 미칩니다.C1 및 C2 컴파일러를 비활성화하면 170MB의 메모리가 줄어듭니다.
Graal VM 컴파일러(C2 대체)를 사용하면 메모리 설치 공간이 다소 줄어듭니다.외부 JVM 메모리보다 코드 메모리 공간이 20MB 증가하고 60MB 감소합니다.
JVM용 Java Memory Management 기사에서는 다양한 메모리 공간에 대한 관련 정보를 제공합니다.Oracle은 네이티브 메모리 추적 문서에서 몇 가지 세부 정보를 제공합니다.고급 컴파일 정책 및 비활성 C2의 컴파일 수준에 대한 자세한 내용은 코드 캐시 크기를 5배 줄입니다.JVM이 Linux 프로세스 상주 세트 크기보다 더 많은 커밋된 메모리를 보고하는 이유에 대한 자세한 내용은 무엇입니까?두 컴파일러가 모두 비활성화되어 있는 경우.
자바에는 많은 메모리가 필요합니다.JVM 자체를 실행하려면 많은 메모리가 필요합니다.힙은 가상 시스템 내에서 애플리케이션에서 사용할 수 있는 메모리입니다.JVM은 가능한 모든 기능이 포함된 큰 번들이기 때문에 로드하는 데만 많은 메모리가 필요합니다.
Java 9부터는 Project Jigsaw라고 불리는 것이 있어 Java 앱을 시작할 때 사용하는 메모리(시작 시간과 함께)를 줄일 수 있습니다.꼭 필요한 메모리를 줄이기 위해 프로젝트 직소나 새로운 모듈 시스템을 만든 것은 아니지만, 중요한 경우에는 시도해 볼 수 있습니다.
https://steveperkins.com/using-java-9-modularization-to-ship-zero-dependency-native-apps/ 의 예를 참조할 수 있습니다.모듈 시스템을 사용하여 21MB의 CLI를 적용했습니다(JRE가 내장되어 있습니다.JRE는 200MB 이상 소요됩니다.이것은, 애플리케이션이 기동하고 있을 때에 할당된 메모리가 적어집니다(많은 미사용 JRE 클래스가 로드되지 않게 됩니다).
여기 또 다른 좋은 튜토리얼이 있습니다.https://www.baeldung.com/project-jigsaw-java-modularity
이것에 시간을 들이고 싶지 않은 경우는, 메모리를 증설하는 것만으로 충분합니다.가끔은 그게 최고일 때도 있어요.
도커 메모리 제한을 올바르게 사이징하는 방법잠시 동안 모니터링하여 응용 프로그램을 확인합니다.컨테이너의 메모리를 제한하려면 도커 실행 명령에 -m, --memory bytes 옵션을 사용하거나, 실행 중인 경우 다음과 같은 동등한 옵션을 사용하십시오.
docker run -d --name my-container --memory 500m <iamge-name>
다른 질문에는 대답할 수 없습니다.
언급URL : https://stackoverflow.com/questions/53451103/java-using-much-more-memory-than-heap-size-or-size-correctly-docker-memory-limi
'itsource' 카테고리의 다른 글
XAMPP에서 2개의 PHP 버전을 사용하는 방법이 있습니까? (0) | 2022.11.14 |
---|---|
기존 테이블에 고유 키를 추가하는 방법(비고유 행 포함) (0) | 2022.11.05 |
mysql 액세스 거부 이벤트허가 권한이 존재함 (0) | 2022.11.05 |
객체가 비어 있습니까? (0) | 2022.11.05 |
IF 문을 사용할 때 MariaDB 구문 오류 발생 (0) | 2022.11.05 |