Java 读取非常大的文件时出现异常>;300 MB
我的任务是以读写模式打开一个大文件,我需要通过搜索起始点和结束点来搜索文件中的部分文本。 然后我需要将搜索到的文本区域写入一个新文件,并从原始文件中删除该部分 以上过程我会多做几次。 因此,我认为对于这些过程,通过CharBuffer将文件加载到内存将很容易,并且可以通过MATCHER类轻松搜索。 但是我在阅读时遇到了HeapSpace异常,尽管我通过执行下面的命令增加到了900MB java-Xms128m-Xmx900m可读大文件 我的代码是Java 读取非常大的文件时出现异常>;300 MB,java,file,Java,File,我的任务是以读写模式打开一个大文件,我需要通过搜索起始点和结束点来搜索文件中的部分文本。 然后我需要将搜索到的文本区域写入一个新文件,并从原始文件中删除该部分 以上过程我会多做几次。 因此,我认为对于这些过程,通过CharBuffer将文件加载到内存将很容易,并且可以通过MATCHER类轻松搜索。 但是我在阅读时遇到了HeapSpace异常,尽管我通过执行下面的命令增加到了900MB java-Xms128m-Xmx900m可读大文件 我的代码是 FileChannel fc = new Fil
FileChannel fc = new FileInputStream(fFile).getChannel();
CharBuffer chrBuff = Charset.forName("8859_1").newDecoder().decode(fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()));
对于上述代码,每个
建议我说这是个坏主意
将所有内容加载到内存中,如果
文件大小为300 MB意味着
600MB由于字符集
以上是我的任务,现在给我建议一些有效的方法。
请注意,我的文件大小将更大,使用JAVA我只需要做这些事情
先谢谢你 您的搜索模式是否匹配多行?如果不是,那么最简单的解决方案是逐行读取:)。真的很简单
但是如果搜索模式匹配多行,那么您需要让我们知道,因为逐行搜索不起作用。您的搜索模式匹配多行吗?如果不是,那么最简单的解决方案是逐行读取:)。真的很简单
但是如果搜索模式匹配多行,那么您需要让我们知道,因为逐行搜索将不起作用。您肯定不希望使用Java将300MB文件加载到单个大缓冲区中。您处理大型文件的方式应该比使用普通I/O更有效,但是当您对映射到内存中的整个文件运行
Matcher
时,很容易耗尽内存
首先,代码内存将文件映射到内存中。。。当文件mmap
插入虚拟地址空间时,这将在虚拟地址空间中消耗300兆内存,尽管这在堆之外。(请注意,300兆的虚拟地址空间被占用,直到MappedByteBuffer
被垃圾收集。请参阅下面的讨论。的JavaDoc警告您这一点。)接下来,您创建一个ByteBuffer
备份文件。这应该没问题,因为它只是mmap
ed文件的一个“视图”,因此应该占用最少的额外内存。它将是堆中的一个小对象,带有指向堆外一个大对象的“指针”。接下来,您将其解码为CharBuffer
,这意味着您对300 MB缓冲区进行了复制,但由于char
是2个字节,因此您进行了600 MB复制(在堆上)
为了响应一条注释,并查看JDK源代码,当您按OP的方式调用map()
时,实际上是将整个文件映射到内存中。查看openJDK 6 b14 Windows本机代码sun.nio.ch.FileChannelImpl.c
,它首先调用,然后调用。查看此源代码,如果您要求将整个文件映射到内存中,此方法将完全按照您的要求执行。引用MSDN:
映射文件使文件的指定部分在
调用进程的地址空间
对于大于地址空间的文件,只能映射一小部分
一次读取文件数据的一部分。第一个视图完成后,可以取消映射并
映射新视图
OP调用map的方式是,文件的“指定部分”是整个文件。这不会导致堆耗尽,但会导致虚拟地址空间耗尽,这仍然是一个OOM错误。这会彻底杀死您的应用程序,就像耗尽堆一样
最后,当您制作一个匹配器
时,匹配器
可能会制作这个600 MB字符缓冲区
的更多副本,具体取决于您如何使用它。哎哟这是少量对象使用的大量内存!给定一个Matcher
,每次调用toMatchResult()
,您都将创建字符串的副本,复制整个
。另外,每次调用replaceAll()
,充其量只能复制整个CharBuffer
的字符串。在最坏的情况下,您将生成一个StringBuffer
,它将缓慢扩展到replaceAll
结果的完整大小(对堆施加大量内存压力),然后从中生成一个String
因此,如果对300 MB文件调用Matcher
上的replaceAll
,找到匹配项,则首先生成一系列更大的StringBuffer
s,直到得到一个600 MB的文件。然后您将制作此StringBuffer
的String
副本。这可能会快速而容易地导致堆耗尽
底线是:Matcher
s不适合处理非常大的缓冲区。你可以很容易地,而且不需要计划,制作很多非常大的物体。我是在做一些与您正在做的事情非常相似的事情时发现这一点的,并且遇到内存耗尽的情况,然后查看Matcher
的源代码
注意:没有unmap
调用。调用后,由MappedByteBuffer
绑定的堆外虚拟地址空间将被固定在那里,直到MappedByteBuffer
被垃圾收集。因此,在MappedByteBuffer
被垃圾收集之前,您将无法对文件执行某些操作(删除、重命名等)。如果在不同的文件上调用map的次数足够多,但堆中没有足够的内存压力来强制使用garb
FileChannel fc = new FileInputStream(fFile).getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
CharBuffer chrBuff = mbb.asCharBuffer();