Bitmap.Save,巨大的内存泄漏
我有一个应用程序,我正在获取位图并使用 GZipStream 压缩它并通过套接字发送它,所有这些都在内存中。 我已经将肮脏的渣男内存泄漏追踪到以下行:
frame.Save(inStream, jpegCodec, parameters);
浏览良好的信息高速公路,我发现了许多有关 Image 类在各种编解码器的 save 方法中泄漏内存的主题。 问题是我实际上找不到任何修复方法。 所以我的问题如下:
- 是什么导致了这个问题
- 我该如何解决这个问题
这是我的 FrameStream 类中泄漏所在的完整 Write() 方法。
/// <summary>
/// Writes a frame to the stream
/// </summary>
/// <param name="frame">The frame to write</param>
public void Write(Bitmap frame) {
using (EncoderParameter qualityParameter = new EncoderParameter(Encoder.Quality, 50L)) {
using (EncoderParameters parameters = new EncoderParameters(1)) {
parameters.Param[0] = qualityParameter;
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo jpegCodec = null;
foreach (ImageCodecInfo codec in codecs) {
if (codec.MimeType == "image/jpeg") {
jpegCodec = codec;
break;
}
}
using (MemoryStream inStream = new MemoryStream()) {
frame.Save(inStream, jpegCodec, parameters); // HUUUGE Memory Leak
Byte[] buffer = new Byte[inStream.Length];
inStream.Read(buffer, 0, buffer.Length);
using (MemoryStream outStream = new MemoryStream()) {
using (GZipStream gzipStream = new GZipStream(outStream, CompressionMode.Compress)) {
gzipStream.Write(buffer, 0, buffer.Length);
}
Byte[] frameData = outStream.ToArray();
Byte[] packet = new Byte[15 + frameData.Length];
Byte[] frameLength = BitConverter.GetBytes(frameData.Length);
Array.Copy(frameLength, 0, packet, 0, frameLength.Length);
Array.Copy(frameData, 0, packet, 15, frameData.Length);
m_Socket.Send(packet);
}
}
}
}
}
I have an application where I am taking a bitmap and compressing it using a GZipStream and sending it over a socket, all in memory. I have tracked down the dirty scumbag memory leak to the following line:
frame.Save(inStream, jpegCodec, parameters);
Browsing around the good ol' information superhighway I have found numerous topics about the Image class leaking memory in the save method on various codecs. Problem is there aren't really any fixes out there that I could find. So my questions are as follows:
- What causes this
- How can I fix this
Here is my full Write() method in my FrameStream class where the leak is located.
/// <summary>
/// Writes a frame to the stream
/// </summary>
/// <param name="frame">The frame to write</param>
public void Write(Bitmap frame) {
using (EncoderParameter qualityParameter = new EncoderParameter(Encoder.Quality, 50L)) {
using (EncoderParameters parameters = new EncoderParameters(1)) {
parameters.Param[0] = qualityParameter;
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo jpegCodec = null;
foreach (ImageCodecInfo codec in codecs) {
if (codec.MimeType == "image/jpeg") {
jpegCodec = codec;
break;
}
}
using (MemoryStream inStream = new MemoryStream()) {
frame.Save(inStream, jpegCodec, parameters); // HUUUGE Memory Leak
Byte[] buffer = new Byte[inStream.Length];
inStream.Read(buffer, 0, buffer.Length);
using (MemoryStream outStream = new MemoryStream()) {
using (GZipStream gzipStream = new GZipStream(outStream, CompressionMode.Compress)) {
gzipStream.Write(buffer, 0, buffer.Length);
}
Byte[] frameData = outStream.ToArray();
Byte[] packet = new Byte[15 + frameData.Length];
Byte[] frameLength = BitConverter.GetBytes(frameData.Length);
Array.Copy(frameLength, 0, packet, 0, frameLength.Length);
Array.Copy(frameData, 0, packet, 15, frameData.Length);
m_Socket.Send(packet);
}
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在处理完位图后,您应该将位图设置为空。
另外,您可能希望在处理位图后调用垃圾收集器(即使这是一个昂贵的操作):
GC.Collect();
位图保存非托管资源 - GC 并不总是“开启”球”与这些。 这是一个关于 Bitmap 类的有趣链接(从紧凑框架的角度来看):http://blog.opennetcf.com/ctacke/PermaLink,guid,987041fc-2e13-4bab-930a-f79021225b74.aspx
You should set the bitmap to null when done working with it, after disposal.
Also, you may want to invoke the Garbage Collector after disposal of the Bitmap (even though it is an expensive operation):
GC.Collect();
Bitmap holds unmanaged resources - the GC is not always "on the ball" with these. Here is an interesting link regarding the Bitmap class (from the perspective of the compact framework): http://blog.opennetcf.com/ctacke/PermaLink,guid,987041fc-2e13-4bab-930a-f79021225b74.aspx
我对套接字了解不多。 但是,我知道阻止 Image 类内存泄漏的一种方法是冻结位图。 希望这篇文章可能会为您提供更多信息。 MemoryStream.Dispose 会失败吗? 从而造成内存泄漏。
I don't know much about sockets. However, I know one way to stop a memory leak with the Image class is to freeze the bitmap. Hopefully, this post might provide you with some more information. Could the MemoryStream.Dispose be failing? Thereby, creating a memory leak.
您是否正在调用 Graphics 对象的 .Dispose() 方法? 这将导致内存泄漏。 编辑:一旦写入了 byte[],您现在就可以清楚地了解 Bitmap 对象的 .Dispose() 了。
Are you calling the .Dispose() method of your Graphics object? That will cause a memory leak. EDIT: Once you have written the byte[], you are now clear to .Dispose() of the Bitmap object.
我建议在 CLR Profiler 下运行代码来定位泄漏源。 如果它是任何类型的托管对象(甚至是非托管资源),只要错误不是由于托管类型泄漏非托管句柄造成的,您就能够看到泄漏在哪里。 如果它在框架代码中,您可能可以使用反射和 P/Invoke 来解决它。
例如,
Icon
类型在某些情况下会泄漏 Win32HICON
。 解决方法是使用Icon
公开的句柄,通过 PInvokingDeleteObject
函数来手动处置HICON
。I suggest running your code under CLR Profiler to locate the source of the leak. If it's a managed object of any type (even an unmanaged resource), as long as the bug isn't due to a managed type leaking an unmanaged handle, you'll be able to see where the leak is. If it's in framework code, you can probably work around it using reflection and P/Invoke.
For example, the
Icon
type leaks Win32HICON
s in some circumstances. The workaround for this is to manually dispose theHICON
by PInvoking theDeleteObject
function, using the handle exposed by theIcon
.好吧,在尝试了每个人的想法和想法以及许多其他方法之后。 我终于尝试了简单的方法:
嗯,这有效,内存泄漏得到修复。我尝试强制调用垃圾收集器,手动处理位图,使用 P/Invoke DeleteObject,没有任何效果,但使用 using 语句却有效。 所以这让我想知道我错过的 using 语句背后发生了什么......
Okay, after trying everyones ideas and thoughts and numerous other methods. I finally tried the simple:
And well, this worked and the memory leak is fixed.. I tried forcefully invoking the garbage collector, disposing the bitmap manually, using P/Invoke DeleteObject, nothing worked, but using a using statement did. So this makes me wonder what happens underthehood on a using statement that I'm missing out on....