Imaging.CreateBitmapSourceFromHBitmap中的内存泄漏

时间:2016-08-03 07:41:02

标签: c# windows memory-leaks

我有下一个功能(制作截图)

[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#)

2 个答案:

答案 0 :(得分:1)

如您所知,您必须Dispose() screenBmp

您实际上是通过using语句调用它,以便 可以,但我怀疑try / catch可能会干扰。

您是否有机会移动try / catch以便仅包围CopyFromScreenCreateBitmapSourceFromHBitmap

来自评论

由于只有在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并手动处理分配。