C# 内存泄漏,但在哪里?

C# 内存泄漏,但在哪里?,c#,wpf,memory-leaks,C#,Wpf,Memory Leaks,我不明白这里漏了什么 using GDI = System.Drawing; public partial class MainWindow : Window { [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr obj); public MainWindow() { InitializeComponent();

我不明白这里漏了什么

using GDI = System.Drawing;

public partial class MainWindow : Window
{
    [DllImport("gdi32.dll")]
    private static extern bool DeleteObject(IntPtr obj);

    public MainWindow()
    {
        InitializeComponent();

        var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(50) };
        timer.Tick += (s, a) =>
        {
            using (var bitmap = new GDI.Bitmap(1000, 1000))
            {
                var hbitmap = bitmap.GetHbitmap();
                var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                image.Freeze();
                DeleteObject(hbitmap);
            }
        };
    timer.Start();
}
位图?处置<代码>hbitmap?删除<代码>图像?已冻结且不可
IDisposable

事实上,此应用程序将崩溃(在我的PC上运行约20秒后)

System.Drawing.dll中发生类型为“System.OutOfMemoryException”的未处理异常

其他信息:内存不足


有什么想法吗?

垃圾收集器释放已处理位图的速度可能不够快。您每20秒创建400个1000*1000位图,这可能会消耗多达1.6GB的内存。也许可以尝试添加第二个计时器,它每1000毫秒运行一次,调用GC.Collect()。

据我所知,没有泄漏发生。问题是你分配大型C#对象的速度太快,而垃圾收集器的启动太晚了

以下是几个相关主题:

下面是有用的线索:

如果启动GC.Collect(第0..3代),内存消耗将得到修复:

    while (true)
    {
        Thread.Sleep(5);

        using (var bitmap = new GDI.Bitmap(1000, 1000))
        {
            var hbitmap = bitmap.GetHbitmap();
            var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());

            image.Freeze();


          DeleteObject(hbitmap);
        }

        Console.WriteLine("Current memory consumption" + GC.GetTotalMemory(false));
        GC.Collect(3);
    }
和输出:

Current memory consumption156572
Current memory consumption156572
Current memory consumption156572
Current memory consumption156572
真正的问题是GC不知道您的非托管分配,即使您释放了它们。您需要增加内存压力,并让GC知道这一点:

 var width = 1000;
                var height = 1000;

                using (var bitmap = new GDI.Bitmap(width, height))
                {
                    var hbitmap = bitmap.GetHbitmap();
                    var allocatedSize = width*height*4; // each pixel takes ~4 bytes?!
                    GC.AddMemoryPressure(allocatedSize);

                    var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty,
                        BitmapSizeOptions.FromEmptyOptions());

                    image.Freeze();


                    DeleteObject(hbitmap);
                    GC.RemoveMemoryPressure(allocatedSize);
                }

让GC知道底层非托管内存有助于确保GC在正确的位置启动。

这是一个非常快速的循环,在您尝试删除它之前,您是否可能没有获得
hbitmap
?要检查
null
或什么?使用内存分析器?/\profiler听起来不错,为了测试(仅!),可能需要添加
Thread.Sleep
@SriramSakthivel,我认为给出的示例足以让更有经验的人告诉我问题出在哪里。我有速成版,对于内存分析器在这种情况下能告诉我什么,我的知识非常有限……第二个计时器可能运行得不够快,但感谢你的一个想法(实际上是正确的,代码中没有内存泄漏,简单的分配太多)。这种压力对我来说是新鲜事,谢谢。我真的很喜欢你的第二个解决方案(没有直接的
GC
call),工作很顺利!注意到第二种解决方案的一个缺点:内存分配可能会变得很高(有时甚至会导致
OutOfMemoryException
),然后停止,保持分配的大量内存。计时器不会发生这种情况,只有在用户操作发生更新时才会发生(如果用户移动鼠标,并且在每个
MouseMove
事件中都会重新绘制某些内容,则可能会发生上述问题)。也许与优先事项有关。无论如何,这个问题的解决方案是在定时/计数的基础上使用您的第一个解决方案(trigger
GC.Collect(3)
directly)(例如,每10次重画一次-收集)。Erti+Erti@Erti-感谢您教我极端垃圾收集。这真是太棒了。完美解决我的烦恼。给你一千个好处。