首页 > 解决方案 > Java 内存泄漏:为什么不收集无法访问的对象?

问题描述

我正在尝试为 JDK Nashorn 错误JDK-8229011找到解决方法。

我使用错误票证中链接的脚本重现了该错误,并进行了几次堆转储。我使用 JProfiler 和 Eclipse Memory Analyzer 来找到问题的根源。但是,我总是以无法到达的路径告终,我不明白为什么对象没有被垃圾收集。AFAIS 没有终结器可以让它们保持活力。如果我在 JProfiler 中打开堆转储而不禁用“Full GC on Dump”,JProfiler 甚至会将无法访问的对象丢弃。但是GC本身不会。我用 G1 GC 测试过,但它与 CMS 相同。

通过运行以下脚本和设置,您可以在 1-2 分钟内使用任何 JDK > 8 获得示例转储:-Xmx20M -XX:+HeapDumpOnOutOfMemoryError

public class TestJsMemLeak
{
    public static final class JsJavaUtil
    {
        private long counter = 0;

        public long testFunc()
        {
            return counter++;
        }
    }

    public static void main(final String[] args) throws Exception
    {
        System.setProperty("nashorn.args", "--no-java -doe -ot=false --language=es6 --no-deprecation-warning --lazy-compilation=false");       
        for (long i = 0; true; ++i)
        {
            final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("nashorn");
            scriptEngine.put("JsJavaUtil", new JsJavaUtil());
            scriptEngine.eval("for (var i = 0; i < 10; ++i) {\n" + " JsJavaUtil.testFunc();\n" + "}");
            System.gc();
            if (i % 100 == 0)
            {
                System.out.println(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
            }
        }
    }
}

运行此程序将导致转储包含大约 6-7 MB 无法访问的 java.lang.ClassValue$ClassValueMap

我正在寻找一些提示,是什么让这些物体保持活力。

标签: javamemory-leaksout-of-memorynashorneclipse-memory-analyzer

解决方案


我不知道发生了什么,但是Eclipse Memory Analyzer 的最新开发版本给出了一些有趣的发现。快照比较 - 加载堆转储 1,加载堆转储 2,运行“快照比较的泄漏嫌疑人:包括泄漏嫌疑人和比较两个快照的系统概述。”

问题嫌疑人1

“”加载的一个“java.lang.ClassValue$ClassValueMap”实例占用了+809,280(19.48%)字节。内存在“java.lang.ClassValue$ClassValueMap”的一个实例中累积,由“”加载,占用+809,280(19.48%)字节。

关键字 java.lang.ClassValue$ClassValueMap

组件报告还说:

弱参考统计

一共找到了10007个java.lang.ref.WeakReference对象,其中弱引用了6969个对象。总共 192 B 的 6 个对象仅通过弱引用被保留(保持活动状态)。可能的内存泄漏 总计 124.4 KB 的 5,785 个对象被弱引用,并且还通过弱引用强保留(保持活动状态)。

细节 ”


推荐阅读