You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中计算机视觉场景下的内存泄漏问题求助

WPF BitmapSource 内存泄漏修复方案

问题根源

你的内存泄漏并非来自BitmapSource未释放,而是调用Imaging.CreateBitmapSourceFromHBitmap时,faceBitmap.GetHbitmap()/imageBitmap.GetHbitmap()会生成未托管的GDI位图句柄,这个句柄不会被.NET GC自动回收,必须手动调用Win32 API释放。

修复步骤

  1. 引入DeleteObject方法用于释放GDI句柄(需添加System.Runtime.InteropServices引用)
  2. 每次调用GetHbitmap()后保存返回的IntPtr,创建BitmapSource后立即释放该句柄
  3. 重构重复的转换逻辑,减少冗余代码

修复后的完整代码

using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

private static void UpdateResultingImage(bool onlyShowFace = false)
{
    if ((imageBitmap == null && faceBitmap == null) || ResultingImage == null)
    {
        return;
    }

    // 处理人脸位图转换
    if (faceBitmap != null)
    {
        // 清理旧BitmapSource的关联资源
        if (faceBitmapSource != null)
        {
            faceBitmapSource.ClearValue(BitmapSource.CacheOptionProperty);
            faceBitmapSource = null;
        }

        IntPtr hBitmap = faceBitmap.GetHbitmap();
        try
        {
            faceBitmapSource = Imaging.CreateBitmapSourceFromHBitmap(
                hBitmap,
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());

            faceBitmapSource = ScaleBitmap(faceBitmapSource, ResultingImage.Width, ResultingImage.Height);
        }
        finally
        {
            // 强制释放未托管GDI句柄
            DeleteObject(hBitmap);
        }
    }

    // 处理原图转换
    if (imageBitmap != null)
    {
        // 清理旧BitmapSource的关联资源
        if (imageBitmapSource != null)
        {
            imageBitmapSource.ClearValue(BitmapSource.CacheOptionProperty);
            imageBitmapSource = null;
        }

        IntPtr hBitmap = imageBitmap.GetHbitmap();
        try
        {
            imageBitmapSource = Imaging.CreateBitmapSourceFromHBitmap(
                hBitmap,
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());

            imageBitmapSource = ScaleBitmap(imageBitmapSource, ResultingImage.Width, ResultingImage.Height);
        }
        finally
        {
            // 强制释放未托管GDI句柄
            DeleteObject(hBitmap);
        }
    }

    ResultingImage.Source = onlyShowFace ? faceBitmapSource : imageBitmapSource;
}

// 引入Win32 API释放GDI对象
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteObject(IntPtr hObject);

额外说明

  • 无需手动调用GC.Collect(),释放GDI句柄后,GC会自动回收BitmapSource的托管资源
  • .NET Core/.NET 5+环境下,CreateBitmapSourceFromHBitmap的行为一致,仍需手动释放HBitmap句柄
  • 可将Bitmap转BitmapSource的逻辑封装为单独方法,进一步提升代码可维护性

内容的提问来源于stack exchange,提问作者Gabriel Alabi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 21:17:35