Java 清除集合未释放堆
Java cleared collection not freeing up heap
我目前正在开发一款游戏,选择Java作为我的主要开发平台。我现在有点后悔,因为我遇到了一个我不完全理解的重大内存泄漏。到目前为止,我对 Java 并不陌生,但出于某种原因,我无法解决这个错误。基本问题是我分段加载我的游戏世界,以免使用太多内存。玩家离开某个区域后,将保存在地图中的设置区域从地图中移除。出于某种原因,垃圾收集器在那之后没有删除该区域,并且 ram 花费的数量一直在增加,直到我停止游戏。根据我对垃圾收集器的理解,当不再有线程访问对象时,它应该删除堆变量,这在我的游戏中就是这种情况。
因为我的游戏引擎已经很大很复杂,无法进行实验,所以我做了一个小测试程序来检查我对垃圾收集器的理解是否"garbage ;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,然后将其清除。为了确保不再访问 long,它们所属的线程甚至会停止。但出于某种原因,没有内存释放。
在我作为程序员的职业生涯中,我从来没有像现在这样对一个问题感到如此困惑。我希望我可以在不重写 C++ 中的所有内容的情况下解决这个问题,这真的很糟糕,因为在我到目前为止的几个月里,程序已经变得非常庞大。
我很高兴你们能提供任何帮助,我真的希望我能解决这个问题,
提前谢谢你:)
不要期望 Java 应用程序在垃圾回收后将内存释放回 OS。
JVM 的正常行为是保持堆大小较大,这样它就不需要 运行 经常进行 GC。 "eager" 向 OS 请求更多内存,"reluctant" 还给它。
(但如果 JVM 确定堆太大,它会返回一些内存。最终。我认为在发生这种情况之前你至少需要 2 个完整的 GC 周期......)
但事情是这样的。如果你在 C++ 中做了同样的事情,那么正常的 C++ 分配器很可能不会将内存返回给 OS。当然,如果大数据结构的创建和销毁导致 C++ 堆碎片化,它就无法这样做。
我最近的一个 3D 应用程序的内存也随着时间的推移逐渐变大。
我发现的原因不是几何体/纹理没有被垃圾收集器收集。相反,当我尝试实例化一个新的几何图形时,我使用的库还在几何图形上创建了一个 event listener
,因为事件侦听器处于全局范围内,所以它永远不会被收集。
我手动分离了事件侦听器,收集器正确地完成了它的工作。
希望对您有所帮助
我目前正在开发一款游戏,选择Java作为我的主要开发平台。我现在有点后悔,因为我遇到了一个我不完全理解的重大内存泄漏。到目前为止,我对 Java 并不陌生,但出于某种原因,我无法解决这个错误。基本问题是我分段加载我的游戏世界,以免使用太多内存。玩家离开某个区域后,将保存在地图中的设置区域从地图中移除。出于某种原因,垃圾收集器在那之后没有删除该区域,并且 ram 花费的数量一直在增加,直到我停止游戏。根据我对垃圾收集器的理解,当不再有线程访问对象时,它应该删除堆变量,这在我的游戏中就是这种情况。
因为我的游戏引擎已经很大很复杂,无法进行实验,所以我做了一个小测试程序来检查我对垃圾收集器的理解是否"garbage ;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,然后将其清除。为了确保不再访问 long,它们所属的线程甚至会停止。但出于某种原因,没有内存释放。
在我作为程序员的职业生涯中,我从来没有像现在这样对一个问题感到如此困惑。我希望我可以在不重写 C++ 中的所有内容的情况下解决这个问题,这真的很糟糕,因为在我到目前为止的几个月里,程序已经变得非常庞大。
我很高兴你们能提供任何帮助,我真的希望我能解决这个问题, 提前谢谢你:)
不要期望 Java 应用程序在垃圾回收后将内存释放回 OS。
JVM 的正常行为是保持堆大小较大,这样它就不需要 运行 经常进行 GC。 "eager" 向 OS 请求更多内存,"reluctant" 还给它。
(但如果 JVM 确定堆太大,它会返回一些内存。最终。我认为在发生这种情况之前你至少需要 2 个完整的 GC 周期......)
但事情是这样的。如果你在 C++ 中做了同样的事情,那么正常的 C++ 分配器很可能不会将内存返回给 OS。当然,如果大数据结构的创建和销毁导致 C++ 堆碎片化,它就无法这样做。
我最近的一个 3D 应用程序的内存也随着时间的推移逐渐变大。
我发现的原因不是几何体/纹理没有被垃圾收集器收集。相反,当我尝试实例化一个新的几何图形时,我使用的库还在几何图形上创建了一个 event listener
,因为事件侦听器处于全局范围内,所以它永远不会被收集。
我手动分离了事件侦听器,收集器正确地完成了它的工作。
希望对您有所帮助