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

2024-10-10 09:28:00
admin
原创
387
摘要:问题描述:我们最近对生产系统的观察表明,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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1124  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   79  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   70  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   82  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   74  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用