【问题标题】:Reading large files for a simulation (Java crashes with out of heap space)读取大文件进行模拟(Java 因堆空间不足而崩溃)
【发布时间】:2013-11-07 12:42:21
【问题描述】:

对于学校作业,我需要为内存访问创建一个模拟。首先,我需要阅读 1 个或多个跟踪文件。每个都包含每次访问的内存地址。示例:

0 F001CBAD
2 EEECA89F
0 EBC17910
...

第一个整数表示读/写等,然后是十六进制内存地址。有了这些数据,我应该运行一个模拟。所以我的想法是将这些数据解析为ArrayList<Trace>(现在我使用Java),其中跟踪是一个简单的类,包含内存地址和访问类型(只是一个字符串和一个整数)。之后我计划遍历这些数组列表来处理它们。

问题甚至在解析时,堆空间不足。每个跟踪文件约为 200MB。我最多有 8 个。这意味着我要“缓存”的数据至少为 ~1.6 GB?让我感到困惑的是,我只解析 1 个文件,而根据我的任务管理器,java 正在使用 2GB ...

有什么更好的方法来做到这一点?

可以在Code Review找到代码sn-p

【问题讨论】:

  • 您需要将所有行都保存在内存中才能完成其余的工作,或者是否可以在读取后处理该行,然后丢弃您的 Trace 对象?

标签: java performance io


【解决方案1】:

The answer I gave on codereview is the same 你应该在这里使用一个.....

但是,因为重复似乎没问题,所以我将在此处复制答案。


几乎可以肯定,问题出在Trace 类的结构中,而且是内存效率。您应该确保将instrType 和hexAddress 存储为内存高效结构。 instrType 似乎是 int,这很好,但只需确保在 Trace 类中将其声明为 int。

更可能的问题是 hexAddress 字符串的大小。您可能没有意识到,但字符串因“泄漏”内存而臭名昭著。在这种情况下,您有一个 line 并且您认为您只是从中获取 hexString ......但实际上, hexString 包含整行......是的,真的。比如看下面的代码:

public class SToken {

    public static void main(String[] args) {
        StringTokenizer tokenizer = new StringTokenizer("99 bottles of beer");
        int instrType = Integer.parseInt(tokenizer.nextToken());
        String hexAddr = tokenizer.nextToken();
        System.out.println(instrType + hexAddr);
    }
}

现在,在(我使用 eclipse)你的 IDE 中设置一个断点,然后运行它,你会看到 hexAddr 包含整行的 char[] 数组,它的偏移量为 3 和7 个。

由于字符串子字符串和其他构造的工作方式,它们可以为短字符串消耗大量内存......(理论上内存与其他字符串共享)。结果,您实际上是将整个文件存储在内存中!!!!

至少,您应该将代码更改为:

hexAddr = new String(tokenizer.nextToken().toCharArray());

但更好的是:

long hexAddr = parseHexAddress(tokenizer.nextToken());

【讨论】:

  • 没有看到它是一个 SE 站点,但即使是一个,您也应该提供更多信息,以便答案更完整。
  • thanks 完美,存储时间长。看起来字符串真的在泄漏......如果字符串泄漏,不应该在 Java 中修复它吗?听起来好像没有采取任何措施来解决这个问题?在这种情况下存储很长的作品,但是其他的呢,如果我需要存储相对较短的字符串,你推荐什么?在另一种语言中也会发生吗?例如 Python 或 JavaScript?
  • 只有在新字符串可以访问旧字符串内部的字符串到字符串操作时,字符串才会“泄漏”。在大多数情况下,这对您有利,请考虑 String line = "99 bottles f beer"; 然后 String ninenine = line.substring(0,2) 和 String bob=line.subString(3); 将占用更少的空间,因为它们都共享相同的 char[] 数组。问题是当您丢弃(和垃圾收集)line 和bob 变量时,您会在ninenine 后面留下一个过大的数组。变量。
【解决方案2】:

和 rolfl 一样,我在 code review 中回答了您的问题。对我来说,最大的问题是先将所有内容读入内存,然后再进行处理。你需要读取一个固定的数量,处理它,然后重复直到完成。

【讨论】:

    【解决方案3】:

    尝试使用java.nio.ByteBuffer 类而不是java.util.ArrayList<Trace>。它还应该减少内存使用量。

    class TraceList {
    
        private ByteBuffer buffer;
    
        public TraceList(){
            //allocate byte buffer
        }
    
        public void put(byte operationType, int addres) {
            //put data to byte buffer
        }
    
        public Trace get(int index) {
            //get data from byte buffer by index
            byte type = ...//read type
            int addres = ...//read addres
            return new Trace(type, addres)
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-21
      • 2013-01-03
      • 2021-12-15
      • 1970-01-01
      • 2015-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多