加载 226MB 文本文件时的内存使用情况

Memory usage in loading a 226MB text file

我必须读取这样制作的 226mb 文本文件:

0 25
1 1382
2 99
3 3456
4 921
5 1528
6 578
7 122
8 528
9 81

第一个数字是索引,第二个是值。我想加载一个短读这个文件的向量(8349328个位置),所以我写了这段代码:

    Short[] docsofword = new Short[8349328];

        br2 = new BufferedReader(new FileReader("TermOccurrenceinCollection.txt"));             
        ss = br2.readLine();
        while(ss!=null)
        {
            docsofword[Integer.valueOf(ss.split("\s+")[0])] = Short.valueOf(ss.split("\s+")[1]);  //[indexTerm] - numOccInCollection
            ss = br2.readLine();
        }
    br2.close();

事实证明,整个负载占用了令人难以置信的 4.2GB 内存量。我真的不明白为什么,我期待一个 15MB 的矢量。 感谢您的回答。

这里有多种影响。

首先,您将数组声明为 Short[] 类型而不是 short[]。前者是引用类型,这意味着每个值都被包装到 Short 的一个实例中,消耗了一个完整对象的开销(很可能是 16 个字节而不是两个)。这也会将每个数组槽从两个字节膨胀到参考大小(通常为 4 或 8 个字节,具体取决于堆大小和 32/64 位 VM)。因此,完全填充数组的最小大小约为:8349328 x 20 = 160MB。

您的阅读代码愉快地产生了大量的垃圾对象 - 您再次使用包装器类型 (Integer) 来寻址数组,而简单的 int 就可以做到这一点。那是至少 16 个字节的垃圾,如果是 int 则为零。 String.split 是另一个罪魁祸首,你强制每行编译两个正则表达式,再加上创建两个字符串。那是无数短暂的对象,每行都变成垃圾。所有这些都可以通过多几行代码来避免。

所以你有一个相对需要内存的数组,还有很多垃圾。垃圾内存可以被清理,但是JVM决定什么时候。该决定基于可用的最大堆内存和垃圾收集器参数。如果你没有为任何一个提供参数,JVM 将在它尝试回收垃圾之前愉快地填充你的机器内存。

TLDR:阅读代码效率低下且没有 JVM 参数。

如果文件是由您生成的,请使用 objectOutputStream,这是读取文件的非常简单的方法。

作为@Durandal,相应地更改代码。我在下面给出示例代码。

short[] docsofword = new short[8349328];

    br2 = new BufferedReader(new FileReader("TermOccurrenceinCollection.txt"));             
    ss = br2.readLine();
    int strIndex, index;
    while(ss!=null)
    {
       strIndex = ss.indexOf( ' ' );
       index = Integer.parseInt(ss.subStr(0, strIndex));
       docsofword[index] = Short.parseShort(ss.subStr(strIndex+1));
       ss = br2.readLine();
    }
br2.close();

甚至你还可以进一步优化。我们可以编写自己的方法来代替 indexOf(),当 char 与 space 匹配时,将字符串解析为整数。之后我们将获得 indexOf Space 和 index for get remain string.