我目前正在开发一款游戏,选择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!");
}
如您所见,我基本上只是将一堆 long 保存在 HashSet 中,然后将其清除。为了确保长整数不会再被访问,它们所属的线程甚至会停止。但由于某种原因,没有内存被释放。
在我作为程序员的职业生涯中,我从未像现在这样对问题如此困惑。我希望我能够解决这个问题,而无需用 C++ 重写所有内容,这真的很糟糕,因为到目前为止,在我处理它的几个月里,该程序已经变得异常庞大。
我很高兴你们能提供任何帮助,我真的希望我能解决这个问题, 提前谢谢你:)
不要期望 Java 应用程序在垃圾回收后将内存释放回操作系统。
JVM 的正常行为是保持堆大小较大,这样就不需要经常运行 GC。 它“渴望”从操作系统请求更多内存,并且“不愿意”归还它。
(但是,如果 JVM 确实认为堆太大,它会返还一些内存。最终。在这种情况发生之前,您通常需要至少 2 个完整的 GC 周期,并且行为取决于所使用的 GC、其他 GC 选项设置、JVM 版本等)
但事情是这样的。 如果您在 C++ 中做了同样的事情,那么正常的 C++ 分配器很可能也不会将内存返还给操作系统。 当然,如果大数据结构的创建和销毁导致 C++ 堆碎片,那么它就无法做到这一点。
我最近的一个 3D 应用程序随着时间的推移,内存也逐渐变大。
我发现的原因不是垃圾收集器没有收集几何/纹理。相反,当我尝试实例化一个新的几何图形时,我使用的库还在几何图形上创建了一个
event listener
,因为事件侦听器位于全局范围内,因此永远不会被收集。
我手动分离了事件监听器,收集器正确地完成了它的工作。
希望对你有帮助