在Java中实例化类会导致内存泄漏

在Java中实例化类会导致内存泄漏,java,class,memory-leaks,hashmap,lwjgl,Java,Class,Memory Leaks,Hashmap,Lwjgl,我正在使用LWJGL库用java开发一个游戏,在创建了一个无限地形生成器之后,我遇到了一个巨大的内存泄漏。基本上,地形被分为块,我有一个渲染距离变量,如8。我有一个HashMap,它用它的坐标映射每个创建的地形,由一个自定义的键类表示。然后,对于渲染距离内的每个关键点(坐标),我将检查HashMap是否包含此关键点,如果不包含,我将创建它并将其添加到要渲染的地形列表中。然后,我渲染地形,清除列表,每5秒检查Hashmap的每个关键点,看看它是否仍在渲染距离内,并删除不在渲染距离内的关键点 问题是

我正在使用LWJGL库用java开发一个游戏,在创建了一个无限地形生成器之后,我遇到了一个巨大的内存泄漏。基本上,地形被分为块,我有一个渲染距离变量,如8。我有一个HashMap,它用它的坐标映射每个创建的地形,由一个自定义的键类表示。然后,对于渲染距离内的每个关键点(坐标),我将检查HashMap是否包含此关键点,如果不包含,我将创建它并将其添加到要渲染的地形列表中。然后,我渲染地形,清除列表,每5秒检查Hashmap的每个关键点,看看它是否仍在渲染距离内,并删除不在渲染距离内的关键点

问题是我有两个内存泄漏,我的意思是内存随着时间的推移而增加。为了调试代码,我只需删除每个部分,直到内存停止增加。我发现两个零件导致了两次泄漏:

首先,HashMap没有正确清除对地形的引用。事实上,我一直跟踪HashMap的大小,它从不超过200个元素,即使在生成新地形时内存增长非常快。我想我遗漏了一些关于HashMap的东西,我必须精确地说,我实现了一个hashCode,并将方法等同于Key和Terrain类

其次,创建新的关键点,并在循环中存储对地形的引用,以避免每次需要时都在HashMap中获取地形,这会导致一个很小但明显的内存泄漏

每帧调用一次RefreshChunks,每5秒调用一次deleteUnusedChunks

public void refreshChunks(MasterRenderer renderer,Loader loader, Vector3f cameraPosition) {
        
        int camposX = Math.round(cameraPosition.x/(float)Terrain.CHUNK_SIZE);
        int camposZ = Math.round(cameraPosition.z/(float)Terrain.CHUNK_SIZE);
        campos = new Vector2f(camposX,camposZ);

        for(int x = -chunkViewDist + camposX; x <= chunkViewDist + camposX; x++) {
            for(int z = -chunkViewDist + camposZ; z <= chunkViewDist + camposZ; z++) {
                Key key = new Key(x,z); //Memory increases
                Terrain value = terrains.get(key); //Memory increases
                if(Maths.compareDist(new Vector2f(x,z), campos, chunkViewDist)) {
                    
                    if(value == null) {

                        terrains.put(key, new Terrain(loader,x,z,terrainInfo)); //Memory leak happens if I fill the HashMap
                    }
                    if(!terrainsToRender.contains(value)) {
                        terrainsToRender.add(value);

                    }
                } else {
                    if(terrainsToRender.contains(value)) {
                        terrainsToRender.remove(value);

                    }
                }
            }
        }
        renderer.processTerrain(terrainsToRender);
        
        if(DisplayManager.getCurrentTime() - lastClean > cleanGap) {
            lastClean = DisplayManager.getCurrentTime();
            deleteUnusedChunks();
        }
    }
    
    public void deleteUnusedChunks() {
        List<Key> toRemove = new ArrayList<Key>();
        terrains.forEach((k, v) -> {
            if(!Maths.compareDist(new Vector2f(k.x,k.y), campos, chunkViewDist)){
                toRemove.add(k);
            }
        });
        for(Key s:toRemove) {
            terrains.remove(s);
        }       
    }
地形类哈希代码和equals实现:

public static class Key {

        public final int x;
        public final int y;

        public Key(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Key)) return false;
            Key key = (Key) o;
            return x == key.x && y == key.y;
        }

        @Override
        public int hashCode() {
            int result = x;
            result = 31 * result + y;
            return result;
        }

    }
@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Terrain)) return false;
        Terrain te = (Terrain) o;
        return gridx == te.gridx && gridz == te.gridz;
    }

    @Override
    public int hashCode() {
        int result = gridx;
        result = 31 * result + gridz;
        return result;
    }
我确实遗漏了一些关于hashmap和类的行为,谢谢你的帮助

编辑:

我的问题是: •实例化新的关键类并创建对地形的引用会使内存随时间增加,这是否正常

•为什么在放置新元素和删除旧元素时,HashMap大小保持不变,而内存却随着时间的推移不断增加


编辑2:我尝试生成地形大约10分钟,在某个时候,程序使用了12 GB的ram,但即使我无法让它崩溃,出现内存不足异常,因为生成的越多,添加的ram就越少。但我仍然不知道我的游戏在开始回收之前需要使用最大数量的内存。

你有什么问题?你想让我们找到内存泄漏?或者自己教你怎么做?前者非常困难,即使你提供的信息可能,但后者在这里是无关的。当应用程序运行时,内存使用增加是正常的。应用程序是否最终会因“内存不足错误”而崩溃?Java虚拟机有一个垃圾收集器。它跟踪正在使用的对象和未使用的对象。它最终将删除未使用的对象,这样您就不会耗尽内存。到目前为止,这里没有任何东西表明您存在内存泄漏-只有“内存不足错误”会告诉您是否确实存在内存泄漏。@ThibaultAbry:如何测量内存增加非常重要。如果您所做的只是检入任务管理器,那么这并不是内存泄漏的好迹象。由于垃圾收集器的存在,Java程序可能(而且是正常的)使用比严格意义上的“需要”更多的内存(即,有一些备用内存使垃圾收集更容易)。也可以尝试使用真正的Java专用工具进行内存转储并检查内存泄漏。如果您认为12GB内存太多,不要让JVM使用那么多。使用JVM启动选项来减少内存。你有什么问题?你想让我们找到内存泄漏?或者自己教你怎么做?前者非常困难,即使你提供的信息可能,但后者在这里是无关的。当应用程序运行时,内存使用增加是正常的。应用程序是否最终会因“内存不足错误”而崩溃?Java虚拟机有一个垃圾收集器。它跟踪正在使用的对象和未使用的对象。它最终将删除未使用的对象,这样您就不会耗尽内存。到目前为止,这里没有任何东西表明您存在内存泄漏-只有“内存不足错误”会告诉您是否确实存在内存泄漏。@ThibaultAbry:如何测量内存增加非常重要。如果您所做的只是检入任务管理器,那么这并不是内存泄漏的好迹象。由于垃圾收集器的存在,Java程序可能(而且是正常的)使用比严格意义上的“需要”更多的内存(即,有一些备用内存使垃圾收集更容易)。也可以尝试使用真正的Java专用工具进行内存转储并检查内存泄漏。如果您认为12GB内存太多,不要让JVM使用那么多。使用JVM启动选项来减少内存。