我有下一个功能(制作截图)
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
private Screen SavedScreen { get; } = Screen.PrimaryScreen;
private BitmapSource CopyScreen()
{
try
{
BitmapSource result;
using (
var screenBmp = new Bitmap(SavedScreen.Bounds.Width, SavedScreen.Bounds.Height, PixelFormat.Format32bppArgb))
{
using (Graphics bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(SavedScreen.Bounds.X, SavedScreen.Bounds.Y, 0, 0, screenBmp.Size,
CopyPixelOperation.SourceCopy);
IntPtr hBitmap = screenBmp.GetHbitmap();
//********** Next line do memory leak
result = Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
}
}
return result;
}
catch (Exception ex)
{
//ErrorReporting ($"Error in CopyScreen(): {ex}");
Debugger.Break();
return null;
}
}
并且无法避免因调用Imaging.CreateBitmapSourceFromHBitmap
而导致的内存泄漏。当我在一个循环中调用此函数时,这个内存泄漏对我来说非常重要。
在WPF应用程序中调用(Windows,c#)
答案 0 :(得分:1)
如您所知,您必须Dispose()
screenBmp
。
您实际上是通过using
语句调用它,以便 可以,但我怀疑try / catch可能会干扰。
您是否有机会移动try / catch以便仅包围CopyFromScreen
和CreateBitmapSourceFromHBitmap
?
由于只有在using
语句的结束后你确定screenBmp
可以被处理掉,我才强迫GC collect
有
GC.Collect();
return result;
并且似乎似乎没有泄漏。
这是我的演示
class Program
{
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
private static Screen SavedScreen { get; } = Screen.PrimaryScreen;
private static BitmapSource CopyScreen()
{
//try
//{
BitmapSource result;
using (
var screenBmp = new Bitmap(200, 100))
{
using (Graphics bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(SavedScreen.Bounds.X, SavedScreen.Bounds.Y, 0, 0, screenBmp.Size,
CopyPixelOperation.SourceCopy);
IntPtr hBitmap = screenBmp.GetHbitmap();
bmpGraphics.Dispose();
//********** Next line do memory leak
result = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
//result = null;
}
}
GC.Collect();
return result;
//}
//catch (Exception ex)
//{
// //ErrorReporting ($"Error in CopyScreen(): {ex}");
// Console.WriteLine(ex.Message);
// Debugger.Break();
// return null;
//}
}
static void Main(string[] args)
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(100);
var test = CopyScreen();
}
}
}
答案 1 :(得分:1)
当您使用位图(屏幕大小)时,意味着预期的数据大小超过85000字节。 GC对这些尺寸的物体进行不同的处理。 它被称为LOH。见https://blogs.msdn.microsoft.com/maoni/2016/05/31/large-object-heap-uncovered-from-an-old-msdn-article/,它在4.5 https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/中得到了改进 但问题仍然存在。高频率地计算大型对象会导致应用程序的内存使用量显着增加。还有2个问题导致它:1)GC不能立即工作,它需要时间才能开始释放内存; 2)LOH的碎片化(参见第一篇文章),这就是它没有被释放的原因,这就是为什么你可以看到内存使用量增加的原因。
可能的解决方案: 1)使用服务器GC和concurent GC;手动强制GC。很可能它没有太大帮助。 2)重复使用现有对象(已分配的内存),而不是一直在循环中创建新的位图和图形。 3)切换到直接使用Windows API并手动处理分配。