【问题标题】:Java cleared collection not freeing up heapJava清除集合没有释放堆
【发布时间】:2017-06-30 23:36:40
【问题描述】:

我目前正在开发一款游戏,并选择 Java 作为我的主要开发平台。我现在有点后悔,因为我遇到了一个我不完全理解的重大内存泄漏。到目前为止,我对 Java 并不陌生,但由于某种原因,我无法解决这个错误。基本问题是我将我的游戏世界分段加载,以免使用太多内存。玩家离开一个区域后,保存在地图中的设置区域会从地图中移除。出于某种原因,此后垃圾收集器不会删除该区域,并且内存消耗量只会不断增加,直到我停止游戏。根据我对垃圾收集器的理解,当没有线程访问对象时,它应该删除堆变量,我的游戏就是这种情况。

因为我的游戏引擎已经相当大而且太复杂了,所以我做了一个小测试程序来检查我对垃圾收集器的理解是否是“垃圾;P”。 (我知道我很吝啬,对吧?... sry ^^)

这是程序:

public static void main(String... args) throws InterruptedException {

new Thread(() -> {
    try {
        Set<Long> set = new HashSet<>();

        System.out.println("Saving stuff");
        for (long i = 0; i < 19999999L; i++) {
            set.add(i);
        }

        Thread.sleep(10000);

        System.out.println("Clearing stuff");
        set.clear();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

    Thread.sleep(40000);
    System.out.println("Exiting program!");
}

如您所见,我基本上只是在 HashSet 中保存一堆 long 并在之后清除它。为了确保不再访问 longs,它们所属的线程甚至停止。但是由于某种奇怪的原因,没有释放内存。

在担任程序员期间,我从来没有像现在这样对一个问题如此困惑过。我希望我可以在不重写 C++ 的所有内容的情况下解决这个问题,这真的很糟糕,因为该程序在我迄今为止工作的几个月里变得非常大。

我很高兴你们能提供任何帮助,我真的希望我能解决这个问题, 提前谢谢你:)

【问题讨论】:

  • 关于像这样的玩具程序的事情是 a) 它们不一定会执行您想要显示的内容(这里没有任何东西可以触发 GC,反正您也不能); b) 它们不一定与您的实际代码的行为有关系,因此它们实际上并不能说明实际问题。
  • 根据我对垃圾收集器的理解应该删除堆变量-> 我认为这句话是错误的,特别是应该,没有什么GC应该做的,你只能假设GC可以做一些事情。 GC 行为,语义未定,无法控制。

标签: java memory-leaks garbage-collection set


【解决方案1】:

不要期望 Java 应用程序在垃圾回收之后将内存释放回操作系统。

JVM 的正常行为是保持堆大小较大,这样它就不需要经常运行 GC。向操作系统请求更多内存是“急切的”,而“不愿意”归还。

(但如果 JVM 确实决定堆太大,它会返还一些内存。最终。我认为您至少需要 2 个完整的 GC 周期才能发生这种情况......)

但事情就是这样。如果你在 C++ 中做了同样的事情,那么普通的 C++ 分配器也很有可能不会将内存还给操作系统。如果大数据结构的创建和销毁导致 C++ 堆的碎片化,那么它肯定不会能够做到这一点。

【讨论】:

  • 所以只要我没有遇到 OutOfMemoryException 我就可以了吗?对我来说似乎有点可疑
  • 只要你没有内存泄漏,或者使用过多的内存,你就可以了。 (或多或少。可能还有其他二阶性能考虑因素。)
  • 如果您担心内存使用模式,请使用 VisualVM 或其他一些内存分析器对其进行调查。或者通过打开 JVM 的 GC 日志记录。如果您怀疑自己有内存泄漏,VisualVM 还有助于跟踪应用程序中的内存泄漏。
【解决方案2】:

我最近的一个 3D 应用程序的内存也随着时间的推移逐渐变大。

我发现的原因不是垃圾收集器没有收集几何体/纹理。相反,当我尝试实例化一个新几何图形时,我使用的库还在几何图形上创建了一个event listener,因为事件侦听器位于全局范围内,因此它永远不会被收集。

我手动分离了事件监听器,收集器正确地完成了它的工作。

希望对你有帮助

【讨论】:

  • 另外,System.gc();Runtime.gc(); 如果你真的想测试一些东西,会建议垃圾收集
  • 我目前没有动态网格,因此在它们被推送到 GPU 后不需要离开它们的范围 :) 所以我的堆中没有几何 ^^ 但还是谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 2014-02-20
  • 2017-09-06
  • 2011-07-30
相关资源
最近更新 更多