C# 解压缩字节时引发System.OutOfMemory异常
我正在压缩字节,在解压时再次出现OOM异常。我无法理解为什么我有足够的内存来存储这个错误 数据经过压缩后约为20MB,需要解压缩。但我总是从记忆中得到例外 下面是相同的代码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()) {
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)。