【发布时间】:2021-05-13 10:47:52
【问题描述】:
对于我的应用程序,Java 进程使用的内存远远大于堆大小。
容器运行所在的系统开始出现内存问题,因为容器占用的内存远大于堆大小。
堆大小设置为 128 MB (-Xmx128m -Xms128m),而容器占用最多 1GB 内存。正常情况下需要500MB。如果 docker 容器的限制低于(例如mem_limit=mem_limit=400MB),则该进程会被操作系统的内存不足杀手杀死。
您能解释一下为什么 Java 进程使用的内存比堆多得多吗?如何正确调整 Docker 内存限制?有没有办法减少 Java 进程的堆外内存占用?
我使用来自Native memory tracking in JVM 的命令收集了有关该问题的一些详细信息。
从宿主系统,我得到容器使用的内存。
$ 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
该应用程序是一个使用 Jetty/Jersey/CDI 的 Web 服务器,捆绑在 36 MB 的内存中。
使用以下版本的 OS 和 Java(在容器内)。 Docker 镜像基于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
【问题讨论】:
-
堆是分配对象的地方,但是 JVM 有许多其他内存区域,包括共享库、直接内存缓冲区、线程堆栈、GUI 组件、元空间。您需要查看 JVM 可以有多大,并将限制设置得足够高,以至于您宁愿进程死掉也不愿再使用。
-
看起来 GC 正在使用大量内存。您可以尝试改用 CMS 收集器。看起来 ~125 MB 用于元空间 + 代码,但是如果不缩小代码库,您不太可能将其缩小。承诺的空间接近你的极限,所以它被杀死也就不足为奇了。
-
在哪里/如何设置 -Xms 和 -Xmx 配置?
-
您的程序是否执行许多文件操作(例如创建千兆字节大小的文件)?如果是这样,您应该知道
cgroups将磁盘缓存添加到已使用的内存中——即使它由内核处理并且对用户程序是不可见的。 (请注意,命令ps和docker stats不算磁盘缓存。)
标签: java linux docker memory jvm