Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 解压缩字节时引发System.OutOfMemory异常_C#_Out Of Memory_Deflate - Fatal编程技术网

C# 解压缩字节时引发System.OutOfMemory异常

C# 解压缩字节时引发System.OutOfMemory异常,c#,out-of-memory,deflate,C#,Out Of Memory,Deflate,我正在压缩字节,在解压时再次出现OOM异常。我无法理解为什么我有足够的内存来存储这个错误 数据经过压缩后约为20MB,需要解压缩。但我总是从记忆中得到例外 下面是相同的代码 public byte[] Compress(byte[] data) { byte[] compressArray = null; try { using (MemoryStream memoryStream = new MemoryStream()) {

我正在压缩字节,在解压时再次出现OOM异常。我无法理解为什么我有足够的内存来存储这个错误

数据经过压缩后约为20MB,需要解压缩。但我总是从记忆中得到例外

下面是相同的代码

public byte[] Compress(byte[] data)
{
    byte[] compressArray = null;
    try
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress))
            {
                deflateStream.Write(data, 0, data.Length);
                deflateStream.Close();
            }
            compressArray = memoryStream.GetBuffer();
            memoryStream.Dispose();
        }
    }
    catch (Exception exception)
    {
        LogManager.LogEvent(EventLogEntryType.Error, exception.Message);
        return data;
    }
    finally { GC.Collect(); }
    return compressArray;
}

public static byte[] Decompress_Bytes(byte[] data)// Around 20MB data
{
    byte[] decompressedArray = null;
    try
    {
        using (MemoryStream decompressedStream = new MemoryStream())
        {
            using (MemoryStream compressStream = new MemoryStream(data))
            {
                using (DeflateStream deflateStream = new DeflateStream(compressStream, CompressionMode.Decompress))
                {
                    deflateStream.CopyTo(decompressedStream);// Exception thrown at this line.
                    deflateStream.Close();
                }
                compressStream.Dispose();
            }
            decompressedArray = decompressedStream.GetBuffer();
            decompressedStream.Dispose();
        }
    }
    catch (Exception exception)
    {
        return data;
    }
    finally { GC.Collect(); }

    return decompressedArray;
}
为了更好地理解,下面是堆栈跟踪

at System.IO.MemoryStream.set_Capacity(Int32 value)
at System.IO.MemoryStream.EnsureCapacity(Int32 value)
at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Stream.InternalCopyTo(Stream destination, Int32 bufferSize)
at System.IO.Stream.CopyTo(Stream destination)
at Symtrax.SQConsole.ConsoleConnectClass.Decompress_Bytes(Byte[] data) in c:\Developement\BI\branch_5.0\MapDesignerUNICODE\ConsoleConnector\SQConsole\ConsoleConnectClass.cs:line 3710
我发现了许多与此相关的问题,但似乎没有一个能解决我的问题


由于我的名誉点数较少,我无法发表评论。因此不得不发帖提问。提前感谢。

如评论中所述,您将使用具有不同长度特征的
GetBuffer
获取内部缓冲区,然后只需调用
ToArray

我在您的代码中添加了一些转储语句,以便LINQPad可以揭示发生了什么:

public byte[] Compress(byte[] data)
{
    byte[] compressArray = null;
    data.Length.Dump("initial array length");
    try
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress))
            {
                deflateStream.Write(data, 0, data.Length);
                deflateStream.Close();
            }
            memoryStream.GetBuffer().Length.Dump("buffer compress len");
            compressArray = memoryStream.ToArray();
            compressArray.Length.Dump("compress array len");
            // no need to call Dispose, using does that for you
            //memoryStream.Dispose();
        }
    }
    catch (Exception exception)
    {
        exception.Dump();
        return data;
    }
    finally { GC.Collect(); }
    return compressArray;
}

public static byte[] Decompress_Bytes(byte[] data)// Around 20MB data
{
    byte[] decompressedArray = null;
    try
    {
        using (MemoryStream decompressedStream = new MemoryStream())
        {
            using (MemoryStream compressStream = new MemoryStream(data))
            {
                using (DeflateStream deflateStream = new DeflateStream(compressStream, CompressionMode.Decompress))
                {
                    deflateStream.CopyTo(decompressedStream);// Exception thrown at this line.
                    deflateStream.Close();
                }
                // no need, using does that
                //compressStream.Dispose();
            }
            decompressedStream.GetBuffer().Length.Dump("buffer decompress len");
            decompressedArray = decompressedStream.ToArray();
            decompressedArray.Length.Dump("decompress array len");
            // no need, using does that
            decompressedStream.Dispose();
        }
    }
    catch (Exception exception)
    {
        exception.Dump();
        return data;
    }
    finally { GC.Collect(); }

    return decompressedArray;
}
这是输出:

初始数组长度 248404

缓冲区压缩长度 262144

压缩数组len 189849

缓冲区解压缩len 327680

解压缩数组len 248404

从这些数字中你可以看到,你将有一个非常不同的长度计数。如果Deflate协议允许有额外字节的字节流,那么您就有可能避开这些额外字节

使用
GetBuffer
而不是
ToArray
似乎是有益的,但我希望复制最终数组所需的内存分配和CPU周期是可以忽略的,特别是在内存流被释放的情况下。这一事实实际上减少了一点内存占用

如果仍然坚持重复使用内存流缓冲区,请确保返回并提供缓冲区中的实际长度:

public byte[] Compress(byte[] data, out int len)
{
    byte[] compressArray = null;
    data.Length.Dump("initial array length");
    try
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            // keep the stream open, we need the length!
            using (DeflateStream deflateStream = new DeflateStream(
                                       memoryStream,
                                       CompressionMode.Compress, 
                                       true))
            {
                deflateStream.Write(data, 0, data.Length);
                deflateStream.Close();
            }
            // output length
            len = (int) memoryStream.Length;                
            compressArray = memoryStream.GetBuffer();
        }
    }
    catch (Exception exception)
    {
        exception.Dump();
        len =-1;
        return data;
    }
    finally { GC.Collect(); }
    return compressArray;
}

public static byte[] Decompress_Bytes(byte[] data, ref int len)// Around 20MB data
{
    byte[] decompressedArray = null;
    try
    {
        using (MemoryStream decompressedStream = new MemoryStream())
        {
            // use the overload that let us limit the memorystream buffer
            using (MemoryStream compressStream = new MemoryStream(data,0, len))
            {
                // keep the stream open
                using (DeflateStream deflateStream = new DeflateStream(
                                compressStream, 
                                CompressionMode.Decompress, 
                                true))
                {
                    deflateStream.CopyTo(decompressedStream);// Exception thrown at this line.
                    deflateStream.Close();
                }
            }
            // output length
            decompressedArray = decompressedStream.GetBuffer();
            len = (int) decompressedStream.Length;
        }
    }
    catch (Exception exception)
    {
        exception.Dump();
        return data;
    }
    finally { GC.Collect(); }

    return decompressedArray;
}
如果您使用上述代码,您将不得不这样调用它:

int len;
var cmp = Compress(Encoding.UTF8.GetBytes(sb.ToString()), out len);
var dec = Decompress_Bytes(cmp,ref len);

请注意,要使用
dec
中的字节,只需考虑第一个
len
字节数。实际上,这是通过使用
Array.Copy
来实现的,它击败了这个解决方案,让我们回到了调用
ToArray
的解决方案。…

整个过程使用了多少内存?需要注意的一点是,您不需要使用对
中声明的对象调用
dispose
——另外,我感兴趣的是了解实际进程的内存使用情况,而不是数据量。运行代码时,请查看资源监视器,并查看工作内存使用量的大小。另外-我认为您不需要所有额外的内存流,因为通常这些流彼此非常兼容,所以原因可能只是因为您在内存中“复制”数据的次数太多,您应该使用ToArray()而不是GetBuffer()。查看
GetBuffer
ToArray
之间的差异:
ToArray
将复制数据,
GetBuffer
仅返回内部缓冲区。但是,内部缓冲区可能(远)大于实际数据,因此您需要自行截断。@JDoshi-GetBuffer将返回比实际写入的字节更多的字节,而您没有截断它-因此,您试图解压无效的zip,这就是您遇到麻烦的原因(IMHO)。