前提条件:
- 具有 16 Gb 内存的电脑
- JDK 1.8.x 安装在 Ubuntu 16.10 x64 上。
- 一个标准的基于 Spring 的 Web 应用程序,部署在 Tomcat 8.5.x 上。 Tomcat 配置了下一个参数:
CATALINA_OPTS="$CATALINA_OPTS -Xms128m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=128m -Xss512k -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods -XX :MaxMetaspaceSize=512m -XX:-TieredCompilation -XX:ReservedCodeCacheSize=512m"
- 用于运行负载测试的 JMeter 2.13
- 用于 Java 堆内存使用跟踪的 JProfiler 9.x
top
java 进程内存使用跟踪工具
当我按顺序开始负载测试 3 次时,我观察到(使用 top
)java 进程正在增加已用内存的数量:
- Tomcat 启动后使用 ~1Gb
- 第一次测试运行后,它使用 4.5Gb
- 当所有测试完成后,Tomcat 正在使用 7Gb 的 RAM
一直以来堆大小是有限的,JProfiler 确认 - 堆大小不超过 512Mb。
这是 JProfiler 的截图。底部的红色数字是java进程使用的内存大小(根据top
)。
问题是:为什么 java 进程在工作时总是不断增加内存使用量?
谢谢!
UPD#1:关于可能的重复:他们已确认这只发生在 Solaris 上。
但我使用的是 Ubuntu 16.10。同样,指出的问题也没有可以解释问题原因的答案。
UPD#2:我不得不在停顿后回到这个问题。现在我使用 pmap
工具来转储 java
进程使用的内存。我有三个转储:在测试运行之前,在第一次测试执行之后和在一些 N 测试执行之后。测试它们会为应用程序产生大量流量。所有转储都在这里:https://gist.github.com/proshin-roman/752cea2dc25cde64b30514ed9ed9bbd0 .它们非常大,但最有趣的是第 8 行的堆大小:测试前需要 282.272 Kb
,最后需要 3.036.400 Kb
- 差异超过 10 倍!每次我运行测试时它都在增长。同时堆大小是恒定的(根据 JProfiler/VisualVM)。我有什么选择可以找到这个问题的原因?调试JVM?我试图找到任何方法来“查看”这段内存,但失败了。所以:
- 我能以某种方式识别
[heap]
内存段的内容吗? - java 的这种行为看起来是预期的吗?
我将不胜感激有关此问题的任何提示。谢谢大家!
UPD #3:使用 jemalloc(感谢@ivan 的想法)我得到了下一张图片:
看起来我遇到了与此处描述的几乎相同的问题:http://www.evanjones.ca/java-native-leak-bug.html
UPD #4:目前我发现该问题与 java.util.zip.Inflater/Deflater 有关,并且这些类在我的应用程序的许多地方都使用过。但是对内存消耗的最大影响是与删除 SOAP 服务的交互。我的应用程序使用 JAX-WS 标准的引用实现,它在负载下给出了下一个内存消耗(在 10Gb 之后它具有低精度): 然后我进行了相同的负载测试,但使用了 Apache CXF 实现,它给出了下一个结果: 所以你可以看到 CXF 使用更少的内存并且更稳定(它并没有像 ref.impl. 那样一直在增长)。 最后我在 JDK 问题跟踪器上发现了一个问题 - https://bugs.openjdk.java.net/browse/JDK-8074108 - 这又是关于 zip 库中的内存泄漏,问题还没有解决。所以看起来我无法真正解决我的应用程序中的内存泄漏问题,只能做一些解决方法。
感谢大家的帮助!
最佳答案
我的假设是您在 JProfiler 中收集分配信息/调用堆栈等,而您观察到的 RSS 增长与 JProfiler 将该数据保存在内存中有关。
您可以通过收集较少的信息来验证这是否属实(分析开始时应该有一个屏幕允许您例如不收集对象分配),并查看您是否观察到结果是较小的 RSS 增长。不使用 JProfiler 运行负载测试也是一种选择。
我有一个 similar case在过去。
关于Java进程内存使用量不断增加,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40672443/