Java清除的集合未释放堆

Java清除的集合未释放堆,java,memory-leaks,garbage-collection,set,Java,Memory Leaks,Garbage Collection,Set,我目前正在开发一个游戏,并选择Java作为我的主要开发平台。我现在有点后悔,因为我遇到了一个我不完全理解的重大内存泄漏。到目前为止,我对Java并不陌生,但出于某种原因,我无法理解这个错误。基本的问题是,为了不占用太多内存,我将游戏世界分块加载。玩家离开一个区域后,保存在地图中的设置区域将从地图中删除。出于某种原因,垃圾收集器在那之后不会删除该区域,并且ram的开销一直在增加,直到我停止游戏为止。从我对垃圾收集器的理解来看,当不再有线程访问对象时,它应该删除堆变量,在我的游戏中就是这样 因为我的

我目前正在开发一个游戏,并选择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!");
}
publicstaticvoidmain(String…args)抛出InterruptedException{
新线程(()->{
试一试{
Set=newhashset();
System.out.println(“保存内容”);
用于(长i=0;i<199999l;i++){
增加(i);
}
睡眠(10000);
System.out.println(“清算材料”);
set.clear();
}捕捉(中断异常e){
e、 printStackTrace();
}
}).start();
睡眠(40000);
System.out.println(“退出程序!”);
}
正如您所看到的,我基本上只是在散列集中保存了一堆long,然后将其清除。为了确保long不再被访问,它们所属的线程甚至会停止。但由于某种原因,没有任何记忆可以释放出来

在我担任程序员期间,我从未像现在这样对一个问题如此困惑。我希望我能解决这个问题,而不重写C++中的所有东西,这将是很糟糕的,因为程序在我已经工作的几个月里变得非常的大。

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

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

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

(但是如果JVM确定堆太大,它会返回一些内存。最终。我认为在这种情况发生之前,您至少需要2个完整的GC周期…)


但事情是这样的。如果你在C++中做了同样的事情,那么正常的C++分配器也不会给OS返回内存。当然,如果大数据结构的创建和破坏导致C++堆的碎片,它就无法做到这一点。

< P>最近我的3D应用程序也随着时间的推移而逐渐增大。 我发现的原因不是垃圾收集器没有收集几何体/纹理。相反,当我尝试实例化一个新的几何体时,我使用的库也在几何体上创建了一个
事件侦听器
,因为事件侦听器处于全局范围,所以永远不会被收集

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


希望这会有所帮助

像这样的玩具程序的一点是a)它们不一定会运行你想要显示的内容(这里没有触发GC的东西,反正你也不能);b) 它们不一定与实际代码的行为有关系,因此它们实际上不是实际问题的说明。根据我对垃圾收集器的理解,它应该删除堆变量->我认为这句话是错误的,特别是应该,GC不应该做什么,您只能假设GC可能会做一些事情。GC行为、语义是不确定的,并且无法控制。所以只要我没有遇到OutOfMemoryException,我就可以了?看起来有点可疑,只要你没有内存泄漏,或者使用太多内存,你就没事。(或多或少。可能还有其他第二级性能考虑因素。)如果您关心内存使用模式,请使用VisualVM或其他内存分析器进行调查。或者通过打开JVM的GC日志记录。如果您怀疑自己有内存泄漏,VisualVM也有助于跟踪应用程序中的内存泄漏和
Runtime.gc()会建议进行垃圾收集。我目前没有动态网格,所以在推送到GPU后,它们不需要离开作用域:)所以我的堆中没有几何体^^^,不过还是要谢谢你