加载 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.
我必须读取这样制作的 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.