【问题标题】:Java Memory Leak when checking for empty space in array检查数组中的空白空间时发生Java内存泄漏
【发布时间】:2014-01-22 08:26:02
【问题描述】:

天哪,垃圾收集器。 我遇到了 java outOfMemory 错误。

堆栈跟踪:

[09:44:10] 2014-01-22 09:44:10 [INFO] [STDERR] java.lang.OutOfMemoryError: Java heap space
[09:44:11] 2014-01-22 09:44:10 [INFO] [STDERR]  at net.minecraft.util.AABBPool.getAABB(AABBPool.java:50)
[09:44:11] 2014-01-22 09:44:11 [INFO] [STDERR]  at net.minecraft.block.Block.getCollisionBoundingBoxFromPool(Block.java:602)
[09:44:12] 2014-01-22 09:44:12 [INFO] [STDERR]  at net.minecraft.block.Block.addCollisionBoxesToList(Block.java:568)
[09:44:14] 2014-01-22 09:44:14 [INFO] [STDERR]  at net.minecraft.world.World.getCollidingBoundingBoxes(World.java:1684)
[09:44:15] 2014-01-22 09:44:14 [INFO] [STDERR]  at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:187)
[09:44:17] 2014-01-22 09:44:15 [INFO] [STDERR]  at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:389)
[09:44:18] 2014-01-22 09:44:17 [INFO] [STDERR]  at net.minecraft.server.integrated.IntegratedServerListenThread.networkTick(IntegratedServerListenThread.java:91)
[09:44:19] 2014-01-22 09:44:18 [INFO] [STDERR]  at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:691)
[09:44:20] 2014-01-22 09:44:19 [INFO] [STDERR]  at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:587)
[09:44:22] 2014-01-22 09:44:20 [INFO] [STDERR]  at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:129)
[09:44:23] 2014-01-22 09:44:22 [INFO] [STDERR]  at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:484)
[09:44:24] 2014-01-22 09:44:23 [INFO] [STDERR]  at net.minecraft.server.ThreadMinecraftServer.run(ThreadMinecraftServer.java:16)
[09:44:33] 2014-01-22 09:44:24 [SEVERE] [Minecraft-Server] Encountered an unexpected exception OutOfMemoryError
[09:44:33] java.lang.OutOfMemoryError: Java heap space
[09:44:33]  at net.minecraft.util.AABBPool.getAABB(AABBPool.java:50)
[09:44:33]  at net.minecraft.block.Block.getCollisionBoundingBoxFromPool(Block.java:602)
[09:44:33]  at net.minecraft.block.Block.addCollisionBoxesToList(Block.java:568)
[09:44:33]  at net.minecraft.world.World.getCollidingBoundingBoxes(World.java:1684)
[09:44:33]  at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:187)
[09:44:33]  at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:389)
[09:44:33]  at net.minecraft.server.integrated.IntegratedServerListenThread.networkTick(IntegratedServerListenThread.java:91)
[09:44:33]  at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:691)
[09:44:33]  at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:587)
[09:44:33]  at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:129)
[09:44:33]  at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:484)
[09:44:33]  at net.minecraft.server.ThreadMinecraftServer.run(ThreadMinecraftServer.java:16)

使用 Eclipse Memory Analyzer 收集的信息:

The thread net.minecraft.server.ThreadMinecraftServer @ 0xbdf43f30 Server thread keeps local variables with total size 935,606,320 (87.86%) bytes.

The memory is accumulated in one instance of "java.lang.Object[]" loaded by "<system class loader>".

Keywords
java.lang.Object[]

Shortest Paths To the Accumulation Point
Class Name  Shallow Heap    Retained Heap
|-> java.lang.Object[17176655] @ 0xee3868b8    68,706,640   935,599,248
\-> elementData java.util.ArrayList @ 0xc22cbc80    24  935,599,272
-\-> listAABB net.minecraft.util.AABBPool @ 0xbff74570    40    935,599,312
--\-> value java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0xc1844bc0    32   935,599,344
---\-> [58] java.lang.ThreadLocal$ThreadLocalMap$Entry[64] @ 0xbe0330b0    272  935,605,248
----\-> table java.lang.ThreadLocal$ThreadLocalMap @ 0xbe036680    24   935,605,272
-----\-> threadLocals net.minecraft.server.ThreadMinecraftServer @ 0xbdf43f30 Server thread Thread    120   935,606,320

Accumulated Object by class:
net.minecraft.util.AxisAlignedBB | Number of Objects: 13,545,197 | Used Heap Size: 866,892,608 | Retained Heap Size: 866,892,608

导致这个 outOfMemory 错误的函数是这样的:

private static int getNextEmptyId()
{
    int temp = 0;
    for(; temp < blocksList.length; temp++)
    {
        if(blocksList[temp] == null)
        {
           break;
        }
    }
    return temp;
}

blocksList 是一个部分填充的数组,大小为 4096。 该函数应该检查列表中的第一个位置是否为空(null),因此它可以将此 id 传递给超级函数,该函数将 id 提供给使用此 id 创建对象并将其添加到 blocksList 的基类。

这里调用了getNextEmptyId()函数:

public class ModBlock extends Block
{

...

    public ModBlock(String name, Material material)
    {
        super(getNextEmptyId(), material);
        GameRegistry.registerBlock(this, name);
        this.setTextureName("terracraft/" + name);
        this.setUnlocalizedName(name);
        this.stepSound = soundStoneFootstep;
    }

}

然后再次通过 super: 调用此函数:

public Block(int par1, Material par2Material)
{
    this.stepSound = soundPowderFootstep;
    this.blockParticleGravity = 1.0F;
    this.slipperiness = 0.6F;

    if (blocksList[par1] != null)
    {
        throw new IllegalArgumentException("Slot " + par1 + " is already occupied by " + blocksList[par1] + " when adding " + this);
    }
    else
    {
        this.blockMaterial = par2Material;
        blocksList[par1] = this;
        this.blockID = par1;
        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
        opaqueCubeLookup[par1] = this.isOpaqueCube();
        lightOpacity[par1] = this.isOpaqueCube() ? 255 : 0;
        canBlockGrass[par1] = !par2Material.getCanBlockGrass();
    }
}


在 AABBPool 第 50 行:

44    public AxisAlignedBB getAABB(double par1, double par3, double par5, double par7, double par9, double par11)
45    {
46        AxisAlignedBB axisalignedbb;
47
48        if (this.nextPoolIndex >= this.listAABB.size())
49        {
50            axisalignedbb = new AxisAlignedBB(par1, par3, par5, par7, par9, par11);
51           this.listAABB.add(axisalignedbb);
52        }
53        else
54        {
55            axisalignedbb = (AxisAlignedBB)this.listAABB.get(this.nextPoolIndex);
56            axisalignedbb.setBounds(par1, par3, par5, par7, par9, par11);
57        }
58
59        ++this.nextPoolIndex;
60        return axisalignedbb;
61    }

在第 602 行:

600    public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
601    {
602        return AxisAlignedBB.getAABBPool().getAABB((double)par2 + this.minX, (double)par3 + this.minY, (double)par4 + this.minZ, (double)par2 + this.maxX, (double)par3 + this.maxY, (double)par4 + this.maxZ);
603    }

如果您需要更多信息,请直说。 感谢您的帮助。

【问题讨论】:

  • 该函数不分配内存*,因此它不能成为 OOM 异常的来源。 *从技术上讲,它在堆栈上分配了一个 4 字节的 int。
  • 嗯,它起着巨大的作用,因为将其注释掉将不再导致内存泄漏.. 我要添加更多代码
  • 发布实际的堆栈跟踪。
  • 它正在调用超类的ModBlock 方法 - 您是否在编辑时输入错误名称?
  • 请告诉我们net.minecraft.util.AABBPool.getAABB(AABBPool.java:50)

标签: java arrays memory memory-leaks minecraft


【解决方案1】:

乍一看,您的问题不是在块列表中寻找间隙的函数,而是您构建的每个块都在该列表和您的 GameRegistry 中被引用。

当您尝试释放一个块时,其中一个引用可能会以某种方式持续存在,这会阻止 GC 擦除其内存并导致您的泄漏......这只是一个想法,请检查一下。

请查看Why does this code sample produce a memory leak? 以获取有关内存泄漏的进一步说明

【讨论】:

  • 我没有在 GameRegistry 中找到任何东西,所以我猜它一定在代码中的其他地方。虽然如果问题出在我没有编写的代码上,更改它可能会导致 mod 之间的不兼容。所以我不得不重新使用计数器来代替..
  • 也许你只需要,当你释放一个块时:如果可能的话,从你的 GameRegistry 中“取消注册”它,并在你的块列表中的位置分配一个空值,再次创建一个间隙。如果该“取消注册”方法存在并且它是干净的,它应该可以让您的 GC 了解它必须清除块
  • 不幸的是,cpw.mods.fml.common.registry.* 中的任何地方都没有这样的功能。此外,如果我将块列表设置为空,它不会尝试创建一个具有重复 ID 的块吗?找到差距不是问题,因为程序启动得很好。
  • 一旦你认为没有必要,你应该确保你在应用程序中存储的对每个对象的每个引用都设置为 null。如果这些引用仍然存在,那就是java中内存泄漏的定义,如答案中的示例所示。一旦您确定您的代码中没有更多引用,请考虑调查 GameRegistry 对您的 Blocks 的引用做了什么,如果您无法修复它,请将错误或其他内容提交给代码所有者跨度>
  • 这真是个好主意。我绝对应该通知 Forge API 开发人员。他们应该最了解整个事情。奇怪的是我之前没有这个想法。好吧,现在我像以前一样拥有一个柜台。
猜你喜欢
  • 2018-07-15
  • 2015-02-27
  • 2017-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-02
  • 1970-01-01
  • 2011-05-21
相关资源
最近更新 更多