使用位读取文件时出现的Byte[]和java.lang.OutOfMemoryError问题

5
我将尝试编写一个按位读取文件的阅读器,但我在处理大型文件时遇到了问题。我尝试读取100mb大小的文件,需要超过3分钟才能完成读取,但是程序可以正常运行。
然而,当我尝试读取500mb大小的文件时,程序甚至无法启动。这是因为以下代码行:
byte[] fileBits = new byte[len];

现在我正在寻找解决方案,但找不到任何答案。也许有人已经解决了这个问题,能够分享一些代码、技巧或想法。

if (file.length() > Integer.MAX_VALUE) {
    throw new IllegalArgumentException("File is too large: " + file.length());
}

int len = (int) file.length();
FileInputStream inputStream = new FileInputStream(file);

try {
    byte[] fileBits = new byte[len];
    for (int pos = 0; pos < len;) {
        int n = inputStream.read(fileBits, pos, len - pos);
        if (n < 0) {
            throw new EOFException();
        }
        pos += n;
    }

inputStream.read(fileBits, 0, inputStream.available());
inputStream.close();

1
你真的需要一次性获取整个文件吗?有什么阻止你进行流式传输呢? - josh.trow
2
你尝试过搜索OutOfMemoryError吗?看看java -mx - Miserable Variable
2
您创建的每个索引都占用4字节。如果有5亿个索引,则会尝试分配2G的空间。除非增加内存或按照josh.trow建议的缓冲/流方式处理,否则无法通过这种方式解决问题。 - John Vint
1
我们采用的解决方案是使用一台带有96GB RAM的机器(使用适当的-XMx选项),而不是24GB,当我们的应用程序内存不足时。 :D.. 说真的,回答一下Josh说的吧。 - Kashyap
@JohnVint 如果创建一个byte数组会使用超过一个字节的索引吗? - mgibsonbr
显示剩余4条评论
4个回答

7

我建议您尝试使用内存映射技术。

FileChannel fc = new FileInputStream(file).getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size());

这将使整个文件几乎立即可用(约10毫秒),并且几乎不使用堆内存。顺便提一下,文件大小必须小于2 GB。


2

如果您真的需要一次性将整个文件加载到内存中,我只能建议增加Java可用的内存。尝试使用Xmx(最大堆大小)或Xms(初始堆大小)参数来调用您的程序(如果您预先知道需要多少内存,则使用后者,否则前者可能更好)。

java -Xms512m -Xmx1g BigApp

作为替代方法,你可以使用NIO的内存映射文件


1

你不应该将整个文件打开到内存中。你需要创建一个固定大小的字节数组缓冲区,然后从你定义的大小的块中打开文件。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接