GZIP解压C#OutOfMemory

GZIP解压C#OutOfMemory,c#,gzip,out-of-memory,compression,gzipstream,C#,Gzip,Out Of Memory,Compression,Gzipstream,我从ftp下载了许多大型gzip文件(大约10MB-200MB)进行解压缩 所以我试着用谷歌搜索并找到一些gzip解压的解决方案 static byte[] Decompress(byte[] gzip) { using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress)) { const int si

我从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.OutMemoryException
  • 解压gzip文件并随后进行文本处理的最佳解决方案是什么
我知道我的对象不能超过2GB,因为我使用32位

这是不正确的。你可以拥有你所需要的内存。32位限制意味着您只能拥有4GB(操作系统占用一半)的虚拟地址空间。虚拟地址空间不是内存。这是一本好书

为什么我得到System.OutMemoryException

因为分配器找不到对象的连续地址空间,或者它发生得太快而阻塞。(很可能是第一个)

解压gzip文件并执行以下操作的最佳解决方案是什么 之后进行一些文本处理


编写一个下载文件的脚本,然后使用gzip或7zip等工具对其进行解压缩,然后对其进行处理。根据处理类型、文件数量和总大小,您必须在某个时候保存它们,以避免此类内存问题。解压后保存它们并立即处理1MB。

您可以尝试以下测试,以了解在获得OutOfMemoryException之前,您可以向MemoryStream写入多少内容:

        const int bufferSize = 4096;
        byte[] buffer = new byte[bufferSize];

        int fileSize = 1000 * 1024 * 1024;

        int total = 0;

        try
        {
            using (MemoryStream memory = new MemoryStream())
            {
                while (total < fileSize)
                {
                    memory.Write(buffer, 0, bufferSize);
                    total += bufferSize;
                }

            }

            MessageBox.Show("No errors"); 

        }
        catch (OutOfMemoryException)
        {
            MessageBox.Show("OutOfMemory around size : " + (total / (1024m * 1024.0m)) + "MB" ); 
        }
const int bufferSize=4096;
字节[]缓冲区=新字节[bufferSize];
int fileSize=1000*1024*1024;
int-total=0;
尝试
{
使用(MemoryStream memory=new MemoryStream())
{
while(总数<文件大小)
{
写入(缓冲区,0,缓冲区大小);
总+=缓冲区大小;
}
}
MessageBox.Show(“无错误”);
}
捕获(OutOfMemoryException)
{
MessageBox.Show(“大约大小的OutOfMemory:+(总计/(1024m*1024.0m))+“MB”);
}
您可能需要先解压缩到一个临时物理文件,然后分小块重新读取它,然后边走边处理


旁白:有趣的是,在WindowsXP PC上,当代码针对.NET2.0时,上面的代码给出了“大约256MB大小的OutOfMemory”,而在.NET4上给出了“大约512MB大小的OutOfMemory”

MemoryStream的内存分配策略对大量数据不友好

因为MemoryStream的契约是将连续数组作为底层存储,所以它必须经常重新分配数组以适应大数据流(通常为log2(大小为\u的\u流))。这种再分配的副作用是

  • 重新分配时的长拷贝延迟
  • 新的阵列必须适合可用的地址空间,因为以前的分配已经严重碎片化
  • 新阵列将位于LOH堆上,该堆有其独特之处(无压缩,在GC2上收集)
因此,通过MemoryStream处理大型(100Mb+)流可能会导致x86系统出现内存不足异常。此外,返回数据的最常见模式是像您这样调用GetArray,这还需要与MemoryStream使用的最后一个数组缓冲区大致相同的空间量

解决方法:

  • 最便宜的方法是将MemoryStream预增长到您需要的大小(最好稍微大一点)。您可以预先计算读取不存储任何内容的伪流所需的大小(浪费CPU资源,但您可以读取它)。还考虑返回流而不是字节数组(或返回内存字节数组的长度与内存流)。
  • 如果需要整个流或字节数组,另一种处理方法是使用临时文件流而不是MemoryStream来存储大量数据
  • 更复杂的方法是实现将底层数据分块成更小(即64K)块的流,以避免在LOH上分配和在流需要增长时复制数据

您是否碰巧在多线程中处理文件?这将消耗大量的地址空间。OutOfMemory错误通常与物理内存无关,因此MemoryStream的运行时间可能比您预期的要早得多。检查此讨论。如果您切换到64位进程,您所处理的文件大小可能会更合适


但在当前情况下,您可以使用内存映射文件来绕过任何地址大小限制。如果您使用的是.NET 4.0,它将为Windows函数提供本机包装。

您正在将流的全部内容加载到内存中,并将其作为字节数组返回。除了内存不足异常,您还希望发生什么?您不应该像这样将其全部加载到内存中--您最终打算如何处理该数组?将其写入文件?不管你想要什么,它都应该是基于流的,而不是基于数组的。。该异常发生在memory.write上,并卡在134217728中。。我不熟悉内存管理,所以请耐心听我说。稍后我会将所有处理过的文件保存到数据库中,gzip文件中的文件是csv fileSure,但如果在解压时对其进行处理,您的设计会更好。这样,您就不必分配大量内存来处理它。(例如,通过将gzip流直接放入
Strea