我目前正在开发游戏并选择Java作为我的主要开发平台。我现在有点后悔,因为我遇到了一个我不完全理解的重大内存泄漏。到目前为止,我不是Java新手,但出于某种原因,我无法解决这个错误。基本问题是我将游戏世界加载成碎片以免使用太多内存。玩家离开某个区域后,设置保存在地图中的区域,从地图中删除。出于某种原因,垃圾收集器在此之后不会删除该区域,并且ram花费的数量一直在增加,直到我停止游戏。根据我对垃圾收集器的理解,当没有线程访问对象时,它应该删除堆变量,这在我的游戏中就是这种情况。
因为我的游戏引擎已经非常庞大而且太复杂而无法试验,我制作了一个小型测试程序来检查我对垃圾收集器的理解是否是“垃圾; 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 ++中的所有内容,因为该程序在我迄今为止所做的工作中已经变得非常重要,所以它真的很糟糕。
我很高兴你能提供任何帮助,我真的希望我能解决这个问题, 提前谢谢你:)
答案 0 :(得分:2)
不要指望Java应用程序在垃圾回收后将内存释放回操作系统。
JVM的正常行为是使堆大小保持较大,以便不需要经常运行GC。它“渴望”从操作系统请求更多内存,并“不愿意”将其还原。
(但是如果JVM确定堆太大了,它会给一些内存回来。最终。我认为在此之前你需要至少2个完整的GC周期......)
但是这就是事情。如果你在C ++中做了同样的事情,那么普通的C ++分配器很可能也不会将内存返回给操作系统。当然,如果大数据结构的创建和破坏导致C ++堆碎片化,它就不会能够这样做。
答案 1 :(得分:0)
我最近的一个3D应用程序也随着时间的推移逐渐增加了内存。
我发现的原因不是垃圾收集器没有收集几何/纹理。相反,当我尝试实例化一个新几何时,我使用的库也在几何上创建了一个event listener
,因为事件监听器处于全局范围,它永远不会被收集。
我手动分离了事件监听器,收集器正确地完成了它的工作。
希望它会有所帮助