【问题标题】:Java - byte buffer Out Of MemoryJava - 字节缓冲区内存不足
【发布时间】:2014-03-14 16:48:44
【问题描述】:

对 Java 有点陌生,但我学得很快。尽管如此,仍有许多方法让我无法理解。我正在编写一个 Java 程序,该程序将运行一堆文件并彻底测试它们以查看它们是否是有效的关卡包文件。感谢 Matt Olenik 和他的 GobFile 类(仅用于提取文件),我能够找到一个很好的策略来获取关卡包的重要部分及其各自的地图文件,以快速确定各种细节。 但是,在 37 个文件(其中 5 个不是关卡包)上对其进行测试,它在测试 35 个文件后崩溃。第 35 个文件是有效的关卡包。它给出的错误是:

线程“主”java.lang.OutOfMemoryError 中的异常:Java 堆空间

     Iterator iter = itemList.keySet().iterator();
     numJKLs = 0;
     while (iter.hasNext()) {
        String nextFile = (String) iter.next();
        if (nextFile.startsWith("jkl\\")) {
           System.out.println(((ItemInfo) itemList.get(nextFile)).length);
-->        byte[] buffer = new byte[((ItemInfo) itemList.get(nextFile)).length];
           gobRaf.seek(((ItemInfo) itemList.get(nextFile)).offset);
           gobRaf.read(buffer, 0,
                 ((ItemInfo) itemList.get(nextFile)).length);
           String[] content = new String(buffer).toLowerCase().split(
                 "\n");
           levels.add(numJKLs, new JKLFile(theFile, nextFile, content));
           gametype = levels.get(numJKLs).getFileType();
           progress.setProgText(nextFile);
           buffer = null;
           content = null;
           numJKLs++;
        }
     }

箭头显示错误标记的位置。 'JKLFile' 类为这些重要部分读取content 数组,但应该(理论上)在完成后处理它。就像这里一样,我在JKLFile 中设置了content = null;,只是为了确保它消失了。如果您想知道它停止的地图文件有多大,那么它设法传递了一个 17 Mb 的地图文件,但这个只有 5 Mb。

如您所见,这些 JKLFile 对象保存在此对象 (GobFile.java) 中以方便以后访问,GobFile 对象保存在另一个类中以供以后访问,直到我更改目录(未实现然而)。但这些物品不应该塞满东西。只是文件名和各种详细信息的列表。

关于如何找出内存去向或哪些对象使用最多资源的任何建议?当我从列表中单击它们时,最好将详细信息保留在内存中,而不必再次加载文件(最多 2 秒)。

/爱德华

【问题讨论】:

  • 您要将所有文件添加到levels?你猜它有多大????
  • @HotLicks 我不会将整个内容添加到levels。没错,我正在发送内容,但我希望它不会被保存。只需处理关键细节并保存 2 个整数和 2 个字符串(级别名称和文件名)。
  • 在我看来,您正在将每个文件的内容添加到 levels -- levels.add(numJKLs, new JKLFile(theFile, nextFile, content));。因此,您可以一次有效地将所有文件读取到存储中(仅以您读取它们的方式,它们占用的空间可能是原始文件的 5 倍)。
  • @HotLicks 好吧,我从整个关卡包中的特定文件中获取的详细信息位于文件中间,前面有未知数量的声明,后面有未知数量的东西.我尝试使用.readLine(),但运行一些文件需要很长时间。这是迄今为止最快的策略。
  • 那么你必须处理所有这些数据填满堆的后果。显然,levels 对象中的每个文件的每一行都有。

标签: java file memory out-of-memory


【解决方案1】:

现在您正在为每个循环创建一个新缓冲区。根据您的垃圾收集,其中一些可能不会从内存中回收/释放,然后您就用完了。您可以使用System.gc() 调用垃圾收集器,但不能保证它在任何给定时间实际运行。

在使用-Xmx-Xms 参数调用程序时,您可以在命令行上增加分配给JVM 的内存。此外,如果您不在 32 位 Windows 上,请确保您使用的是 64 位 JVM,因为 32 位有内存限制,而前者没有。从命令行运行java -version,查看当前系统JVM。

您可以将 byte[] 缓冲区的声明移到循环之外(在您的 while 上方),以便在每次迭代时将其清除并重用。

Iterator iter = itemList.keySet().iterator();
numJKLs = 0;
byte[] buffer;
while (iter.hasNext()) {
    ...
    buffer = new byte[] ...
    ...
}

您正在处理的文件有多大?您真的需要将它们中的每一个都添加到您的levels 变量中吗?你能用一些文件信息或统计数据来代替吗?您的 JVM 目前分配了多少 RAM?

要查看任何给定时间的当前内存使用情况,您需要在程序运行时对其进行调试。查看如何为您选择的 IDE 执行此操作,您将能够在程序的生命周期内跟踪任何对象的当前状态。

【讨论】:

  • 运行 System.gc() 不会阻止错误发生。增加堆大小只是隐藏了这样一个事实,即他一次读取所有文件非常浪费存储空间。
  • 好吧,我实际上并没有将整个 content 添加到我的 levels 变量中。没错,我正在尝试将content 发送给班级,但认为它不会被保存。只需处理关键细节并保存 2 个整数和 2 个字符串(级别名称和文件名)。但是,您的回答确实帮助我重新组织了我的变量。将byte[] buffer 以及String[] content 和一些其他变量移到循环之外使程序完成了所有37 个文件。必须尝试并记住这一点。谢谢。
猜你喜欢
  • 1970-01-01
  • 2012-06-16
  • 2022-11-18
  • 2014-07-12
  • 1970-01-01
  • 1970-01-01
  • 2014-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多