3年前 (2021-01-04)  jvm |   抢沙发  2259 
文章评分 0 次,平均分 0.0

当考虑Java中的内存泄漏时,我们通常会考虑Java堆泄漏,即在堆中分配的对象没有被垃圾收集。这是我在处理一台服务器内存泄漏时的想法,但我即将经历的远超出我的想象。

症状:运行Vertx应用程序(没有交换分区)的生产服务器被Linux内存不足kill掉(操作系统机制,当系统出现内存紧张的情况时释放内存)崩溃。

因为它是生产服务器,所以我认为可以让我们使用堆转储和MAT来检查发生了什么,并尝试找出谁在消耗这么多内存。

结果令人惊讶,java堆是合理的,远远少于进程内存的足迹。有什么东西侵蚀了我的内存,我不知道那是什么。

我的出发点是在home目录中创建的java致命错误日志,所以我开始研究这些日志。您可以在下面的页面中阅读更多关于java致命错误日志的信息:致命错误日志致命错误日志可以提供很多有价值的信息并节省时间,因此我建议您仔细阅读。致命错误日志显示堆大小小于2G,但进程正在增长到大约3-4G字节,这是怎么回事?

Java进程包含以下内存空间:

  • 堆-分配对象的位置。
  • 线程堆栈-包含所有线程堆栈。
  • Metaspace-包含元数据类(替换Java7和更早版本中的PermGen)。
  • 代码缓存-JIT编译器代码缓存。
  • 堆缓冲池不足。
  • 操作系统内存-本机操作系统内存。

我用了命令:

jmap -heap [pid]

它打印了JVM堆大小的摘要,还显示了堆大小约为1.5GByte。

我检查了元空间的大小,但只有几兆字节。也许代码缓存是问题所在?我再次检查了致命错误日志,我看到只有大约20M。我得出的结论是,可能我有本机内存泄漏堆外内存泄漏,我开始检查这个选项。我使用了java NMT特性,通过使用本机内存参数启动应用程序:-XX:NativeMemoryTracking=detail,然后使用jcmd实用程序(包含在JDK中)检查本机内存。在下一页中阅读有关NMT的更多信息:NMT结果包含以下内容:

Internal (reserved=1031767KB, committed=1031767KB)
(malloc=1031735KB #7619)
(mmap: reserved=32KB, committed=32KB)

我发现JVM有大约1Gbyte大小的内存。现在我确信我有本机内存泄漏

我发现有巨大的malloc(操作系统内存分配调用)内存分配,但我仍然不知道是什么原因造成的。我试着用gdb处理内存转储,但效果不好,我得出的结论是它可能不会有用,所以我搜索了Linux内存泄漏检测工具。我有几个候选人:

前两个由于某些原因不起作用,所以尝试了人们推荐的jemalloc。我在应用程序启动脚本中添加了以下行:

MALLOC_CONF=prof_leak:true,prof_final:true,lg_prof_interval:30,lg_prof_sample:17 \
LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 [jre path]/jre/bin/java [app parameters]

一开始jemalloc不起作用,我不得不用configure参数-enable prof重新编译它,然后它就开始工作了。关闭应用程序后,jemalloc在工作目录中创建了reproof文件。

[jemalloc install dir]/bin/jeprof --show_bytes --pdf ‘[jre path]/jre/bin/java' [jeprof file] > 	[pdf output file name]

java堆外内存泄漏排查

jemalloc分析表明,使用malloc os调用分配内存的“Unsafe_AllocateMemory”存在严重泄漏。我原以为我现在就能得到答案,但显然我错了。我在谷歌上搜索了一下,发现不安全的分配内存可能与名为sun.misc.Unsafe是执行本机内存分配的JDK私有类,正如它的名称所表明的那样,它是不安全的(Oracle计划在java9中删除这个类,但最终它仍保留在不受支持的模块中)。我搜索了应用程序代码,但没有找到它的任何用法,我假设它可能是应用程序的一些库使用的。主要嫌疑人是Netty,Netty是Vertx使用的网络库。Netty提供了很好的性能,但它使用本机内存分配来实现这一点。在Netty源代码中挖掘发现它正在使用`sun.misc.Unsafe`分配本机内存池。Netty包含内存泄漏检测机制,因此我尝试使用Netty参数:

-Dio.netty.leakDetection.level=advanced But that dind't supply any output.

我试着通过使用其他netty参数(没有很好的文档)来限制netty:

-Dio.netty.noPreferDirect=true 
-Dio.netty.allocator.type=unpooled 
-Dio.netty.maxDirectMemory=0

但这也不管用。

经过多次尝试,我最终发现直接内存分配可以通过以下jvm参数来限制:

-XX:MaxDirectMemorySize=[max memory]

在深入研究jvm之后,我终于找到了解决方案。我学到了很多关于jvm机制和内存空间的知识。
java堆外内存泄漏排查

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1183.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册