Java 进程的常驻内存使用量 (RSS) 不断增长

2024-10-10 09:28:00
admin
原创
177
摘要:问题描述:我们最近对生产系统的观察表明,Java 容器的常驻内存使用量正在增长。关于这个问题,我们使用 pmap 等一些本机工具进行了一些调查,以了解为什么 java 进程消耗的内存比堆 + 线程堆栈 + 共享对象 + 代码缓存 + 等多得多。结果,我们发现本机进程(可能使用 malloc/mmap)分配了一...

问题描述:

我们最近对生产系统的观察表明,Java 容器的常驻内存使用量正在增长。关于这个问题,我们使用 pmap 等一些本机工具进行了一些调查,以了解为什么 java 进程消耗的内存比堆 + 线程堆栈 + 共享对象 + 代码缓存 + 等多得多。结果,我们发现本机进程(可能使用 malloc/mmap)分配了一些 64M 内存块(成对):

0000000000400000      4K r-x--  /usr/java/jdk1.7.0_17/bin/java
0000000000600000      4K rw---  /usr/java/jdk1.7.0_17/bin/java
0000000001d39000   4108K rw---    [ anon ]
0000000710000000  96000K rw---    [ anon ]
0000000715dc0000  39104K -----    [ anon ]
00000007183f0000 127040K rw---    [ anon ]
0000000720000000 3670016K rw---    [ anon ]
00007fe930000000  62876K rw---    [ anon ]
00007fe933d67000   2660K -----    [ anon ]
00007fe934000000  20232K rw---    [ anon ]
00007fe9353c2000  45304K -----    [ anon ]
00007fe938000000  65512K rw---    [ anon ]
00007fe93bffa000     24K -----    [ anon ]
00007fe940000000  65504K rw---    [ anon ]
00007fe943ff8000     32K -----    [ anon ]
00007fe948000000  61852K rw---    [ anon ]
00007fe94bc67000   3684K -----    [ anon ]
00007fe950000000  64428K rw---    [ anon ]
00007fe953eeb000   1108K -----    [ anon ]
00007fe958000000  42748K rw---    [ anon ]
00007fe95a9bf000  22788K -----    [ anon ]
00007fe960000000   8080K rw---    [ anon ]
00007fe9607e4000  57456K -----    [ anon ]
00007fe968000000  65536K rw---    [ anon ]
00007fe970000000  22388K rw---    [ anon ]
00007fe9715dd000  43148K -----    [ anon ]
00007fe978000000  60972K rw---    [ anon ]
00007fe97bb8b000   4564K -----    [ anon ]
00007fe980000000  65528K rw---    [ anon ]
00007fe983ffe000      8K -----    [ anon ]
00007fe988000000  14080K rw---    [ anon ]
00007fe988dc0000  51456K -----    [ anon ]
00007fe98c000000  12076K rw---    [ anon ]
00007fe98cbcb000  53460K -----    [ anon ]

我将 0000000720000000 3670016K 这一行解释为堆空间,我们使用 JVM 参数“-Xmx”定义其大小。紧接着,对开始,其总和正好是 64M。我们使用的是 CentOS 版本 5.10 (Final) 64 位架构和 JDK 1.7.0_17 。

问题是,这些块是什么?哪个子系统分配这些块?

更新:我们不使用 JIT 和/或 JNI 本机代码调用。


解决方案 1:

也可能存在本机内存泄漏。一个常见问题是由于未关闭ZipInputStream/而导致的本机内存泄漏GZIPInputStream

打开的典型方式ZipInputStream是通过调用Class.getResource/ClassLoader.getResource并调用openConnection().getInputStream()实例java.net.URL或通过调用Class.getResourceAsStream/ ClassLoader.getResourceAsStream。必须确保这些流始终被关闭。

一些常用的开源库存在泄漏未关闭的java.util.zip.Inflaterjava.util.zip.Deflater实例的错误。例如,Nimbus Jose JWT 库已在 6.5.1 版本中修复了相关的内存泄漏。Java JWT (jjwt) 有一个类似的错误,已在 0.10.7 版本中修复。这两个案例中的错误模式是,当提供/实例时,调用DeflaterOutputStream.close()InflaterInputStream.close()不调用Deflater.end()/ 。在这些情况下,仅检查代码中是否关闭了流是不够的。代码中创建的每个/实例都必须有被调用的处理。Inflater.end()`DeflaterInflaterDeflaterInflater.end()`

检查 Zip*Stream 泄漏的一种方法是获取堆转储并搜索名称中带有“zip”、“Inflater”或“Deflater”的任何类的实例。这在许多堆转储分析工具(如 Yourkit Java Profiler、JProfiler 或 Eclipse MAT)中都是可能的。还值得检查处于最终状态的对象,因为在某些情况下,内存仅在最终确定后才释放。检查可能使用本机库的类很有用。这也适用于 TLS/ssl 库。

Elastic有一个名为leakchecker的 OSS 工具,它是一个 Java 代理,可用于查找java.util.zip.Inflater尚未关闭(.end()未调用)的实例的来源。

对于一般的本机内存泄漏(不仅仅是 zip 库泄漏),您可以使用jemalloc来调试本机内存泄漏,方法是通过指定MALLOC_CONF环境变量中的设置来启用 malloc 采样分析。详细说明请参阅此博客文章:http://www.evanjones.ca/java-native-leak-bug.html。此博客文章还包含有关使用 jemalloc 调试 Java 应用程序中的本机内存泄漏的信息。Elastic的一篇博客文章介绍了 jemalloc 并提到了leakchecker,这是 Elastic 开源的工具,用于追踪未关闭的 zip inflater 资源导致的问题。

还有一篇博客文章介绍了与 ByteBuffers 相关的本机内存泄漏。Java 8u102具有特殊的系统属性jdk.nio.maxCachedBufferSize来限制该博客文章中描述的缓存问题。

-Djdk.nio.maxCachedBufferSize=262144

最好始终检查打开的文件句柄,以查看内存泄漏是否由大量 mmap:ed 文件引起。在 Linux 上,lsof可用于列出打开的文件和打开的套接字:

lsof -Pan -p PID

进程内存映射的报告也可以帮助调查本机内存泄漏

pmap -x PID

对于在 Docker 中运行的 Java 进程,应该可以在“主机”上执行 lsof 或 pmap 命令。您可以使用此命令找到容器化进程的 PID

docker inspect --format '{{.State.Pid}}' container_id

获取线程转储(或使用 jconsole/JMX)来检查线程数也很有用,因为每个线程都会为其堆栈消耗 1MB 的本机内存。大量线程会占用大量内存。

JVM 中还有本机内存跟踪 (NMT) 。这可能有助于检查 JVM 本身是否正在耗尽本机内存。

AsyncProfiler 可用于检测本机内存分配的来源。这在另一个答案中进行了解释。

jattach 工具还可用于容器化 (docker) 环境,以从主机触发线程转储或堆转储。它还能够运行控制 NMT 所需的 jcmd 命令。

解决方案 2:

我遇到了同样的问题。这是 glibc >= 2.10 的一个已知问题

解决方法是设置此环境变量
export MALLOC_ARENA_MAX=4

IBM 有关设置 MALLOC_ARENA_MAX 的文章
https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en

用 Google 搜索 MALLOC_ARENA_MAX 或在 SO 上搜索它可以找到很多参考资料。

您可能还希望调整其他 malloc 选项以优化分配内存的低碎片化:

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   681  
  在项目管理领域,集成产品开发(IPD)流程以其高效、协同的特点,被众多企业视为提升产品竞争力的关键。IPD流程强调跨部门、跨职能的紧密合作,以确保产品从概念到市场各个环节的无缝衔接。然而,实现这一目标并非易事,它需要企业深刻理解并掌握IPD流程中的跨部门协作艺术。本文将深入探讨IPD流程中跨部门协作的三个关键点,旨在为...
IPD项目管理咨询   9  
  掌握IPD流程图:提升团队协作的关键路径在当今快速变化的商业环境中,团队协作的效率与效果直接关系到项目的成功与否。集成产品开发(Integrated Product Development,简称IPD)作为一种先进的研发管理理念,通过跨部门、跨领域的协同工作,能够显著提升产品开发的速度与质量。而IPD流程图,则是这一理...
IPD流程阶段   9  
  IPD流程概述:理解其核心价值与实施背景集成产品开发(Integrated Product Development,简称IPD)是一种先进的产品开发管理理念,它强调跨部门协作、市场导向和快速响应变化的能力。IPD流程不仅关注产品本身的技术创新,更注重将市场、研发、生产、销售等各个环节紧密集成,以实现产品从概念到市场的高...
华为IPD是什么   7  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程以其跨部门协作、高效决策和快速响应市场变化的特点,被众多企业视为提升竞争力的关键。然而,实践IPD流程并非易事,项目管理中的种种错误往往阻碍了其效果的充分发挥。本文旨在深入探讨如何在实施IPD流程时避免这些常见错误,...
IPD框架   7  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用