Java进程内存使用量无限增加

前提条件:

  • 带有16 Gb RAM的PC
  • 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用于负载测试运行
  • JProfiler 9.x用于java堆内存使用情况跟踪
  • 用于java进程内存使用情况跟踪的top util
  • 当我按顺序开始加载测试3次时,我观察到(使用top )java进程正在增加一些已用内存:

  • 在Tomcat启动后它使用〜1Gb
  • 在第一次测试运行后,它使用4.5Gb
  • 当所有测试完成时,Tomcat正在使用7Gb的RAM
  • 所有这一次堆大小是有限的JProfiler确认 - 堆大小不超过512Mb。

    这是JProfiler的截图。 底部的红色数字是java进程使用的内存大小(根据top )。 在这里输入图像描述

    问题是: 为什么java进程在工作时始终保持内存使用量的增长?

    谢谢!

    UPD#1:关于可能的重复:他们have confirmed that this only happens on Solaris. 但我使用Ubuntu 16.10。 有针对性的问题也没有答案可以解释问题的原因。

    UPD#2:暂停后我必须回到这个问题。 现在我使用pmap util来创建java进程使用的内存转储。 我有三个转储:在测试运行之前,在第一次测试执行之后以及在一些N次测试执行之后。 测试他们为应用程序产生大量流量。 所有转储都在这里:https://gist.github.com/proshin-roman/752cea2dc25cde64b30514ed9ed9bbd0。 他们是相当巨大的,但最有趣的事情是八号线与堆的大小:它需要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实现,并得到了下一个结果: 使用Apache CXF的内存消耗 所以你可以看到CXF使用更少的内存,并且更稳定(它并不像ref.impl那样增长)。 最后,我在JDK问题跟踪器上发现了一个问题 - https://bugs.openjdk.java.net/browse/JDK-8074108 - 这又是关于zip库中的内存泄漏问题,该问题尚未结束。 所以它看起来像我不能真正解决我的应用程序中的内存泄漏问题,只能做一些解决方法。

    感谢你的帮助!


    我的假设是,您收集JProfiler中的分配信息/调用堆栈/等,您观察到的RSS增长与JProfiler将这些数据保存在内存中有关。

    您可以通过收集较少的信息来验证这是否为真(在分析开始时应该有一个屏幕允许您例如不收集对象分配),并查看您是否观察到较小的RSS增长。 在没有JProfiler的情况下运行负载测试也是一种选择。

    我过去曾有过类似的情况。


    你能用这个选项重新运行你的测试-XX:MaxDirectMemorySize=1024m ? 这个限制的确切值并不重要,但它显示了可能的“泄漏”。

    您还可以提供GC详细信息( -XX:+PrintGC )?

    java.nio.ByteBuffer由于具体的最终化而成为它们的可能原因。

    更新#1

    我看到类似的行为有两个原因:java.misc.Unsafe(不太可能)和高负载的JNI调用。

    没有测试简介就很难理解。

    更新#2

    高负载的JNI调用和finalize()方法都会导致所描述的问题,因为对象没有足够的时间来完成。

    下面的juzip.Inflater片段:

    /**
     * Closes the decompressor when garbage is collected.
     */
    protected void finalize() {
        end();
    }
    
    /**
     * Closes the decompressor and discards any unprocessed input.
     * This method should be called when the decompressor is no longer
     * being used, but will also be called automatically by the finalize()
     * method. Once this method is called, the behavior of the Inflater
     * object is undefined.
     */
    public void end() {
        synchronized (zsRef) {
            long addr = zsRef.address();
            zsRef.clear();
            if (addr != 0) {
                end(addr);
                buf = null;
            }
        }
    }
    
    private native static void end(long addr);
    

    根据Occam的剃须刀:不是因为你有某处存在内存泄漏(即,“无意的对象保留”a'la Effective Java Item 6)吗?

    链接地址: http://www.djcxy.com/p/19847.html

    上一篇: Java process memory usage keeps increasing infinitely

    下一篇: heap memory usage as a Java program runs