在Java中实例化类会导致内存泄漏
我正在使用LWJGL库用java开发一个游戏,在创建了一个无限地形生成器之后,我遇到了一个巨大的内存泄漏。基本上,地形被分为块,我有一个渲染距离变量,如8。我有一个HashMap,它用它的坐标映射每个创建的地形,由一个自定义的键类表示。然后,对于渲染距离内的每个关键点(坐标),我将检查HashMap是否包含此关键点,如果不包含,我将创建它并将其添加到要渲染的地形列表中。然后,我渲染地形,清除列表,每5秒检查Hashmap的每个关键点,看看它是否仍在渲染距离内,并删除不在渲染距离内的关键点 问题是我有两个内存泄漏,我的意思是内存随着时间的推移而增加。为了调试代码,我只需删除每个部分,直到内存停止增加。我发现两个零件导致了两次泄漏: 首先,HashMap没有正确清除对地形的引用。事实上,我一直跟踪HashMap的大小,它从不超过200个元素,即使在生成新地形时内存增长非常快。我想我遗漏了一些关于HashMap的东西,我必须精确地说,我实现了一个hashCode,并将方法等同于Key和Terrain类 其次,创建新的关键点,并在循环中存储对地形的引用,以避免每次需要时都在HashMap中获取地形,这会导致一个很小但明显的内存泄漏 每帧调用一次RefreshChunks,每5秒调用一次deleteUnusedChunks在Java中实例化类会导致内存泄漏,java,class,memory-leaks,hashmap,lwjgl,Java,Class,Memory Leaks,Hashmap,Lwjgl,我正在使用LWJGL库用java开发一个游戏,在创建了一个无限地形生成器之后,我遇到了一个巨大的内存泄漏。基本上,地形被分为块,我有一个渲染距离变量,如8。我有一个HashMap,它用它的坐标映射每个创建的地形,由一个自定义的键类表示。然后,对于渲染距离内的每个关键点(坐标),我将检查HashMap是否包含此关键点,如果不包含,我将创建它并将其添加到要渲染的地形列表中。然后,我渲染地形,清除列表,每5秒检查Hashmap的每个关键点,看看它是否仍在渲染距离内,并删除不在渲染距离内的关键点 问题是
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启动选项来减少内存。