我有许多从 ftp 下载的大型 gzip 文件(大约 10MB - 200MB)需要解压。
所以我尝试谷歌并找到一些gzip解压的解决方案。
static byte[] Decompress(byte[] gzip)
{
using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
{
const int size = 4096;
byte[] buffer = new byte[size];
using (MemoryStream memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
}
while (count > 0);
return memory.ToArray();
}
}
}
它适用于任何低于 50mb 的文件,但一旦我输入超过 50mb,就会出现系统内存不足异常。异常前的最后一个位置和内存长度是134217728。我不认为它与我的物理内存有关系,我知道我不能拥有超过2GB的对象,因为我使用32位。
我还需要在解压文件后处理数据。我不确定内存流是否是最好的方法,但我真的不喜欢写入文件,然后再次读取文件。
我的问题
- 为什么我会收到 System.OutofMemoryException?
- 解压缩 gzip 文件并随后进行一些文本处理的最佳解决方案是什么?
MemoryStream的内存分配策略对于海量数据并不友好。
由于 MemoryStream 的约定是使用连续数组作为底层存储,因此它必须经常为大流重新分配数组(通常为 log2(size_of_stream))。这种重新分配的副作用是
- 重新分配时的长时间复制延迟
- 新数组必须适合已被先前分配严重碎片化的空闲地址空间
- 新数组将位于有其怪癖的 LOH 堆上(没有压缩,在 GC2 上收集)。
因此,通过 MemoryStream 处理大型 (100Mb+) 流可能会在 x86 系统上出现内存不足异常。此外,返回数据的最常见模式是像您一样调用 GetArray,这还需要与用于 MemoryStream 的最后一个数组缓冲区大约相同的空间量。
解决办法:
- 最便宜的方法是预先将 MemoryStream 增长到您需要的近似大小(最好稍微大一些)。您可以通过读取不存储任何内容的假流来预先计算所需的大小(浪费 CPU 资源,但您将能够读取它)。还可以考虑返回流而不是字节数组(或返回 MemoryStream 缓冲区的字节数组以及长度)。
- 如果您需要整个流或字节数组,处理它的另一个选择是使用临时文件流而不是 MemoryStream 来存储大量数据。
- 更复杂的方法是实现将底层数据分块为较小(即 64K)块的流,以避免在流需要增长时在 LOH 上进行分配和复制数据。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)