带有渐变的神秘 Direct2D 内存泄漏

Mysterious Direct2D Memory Leak with gradients

我有一个基于 SharpDX 的 C# 应用程序。有一种场景允许用户编辑线性渐变,但由于我没有找到修改ID2D1GradientStopCollection(a.k.a.SharpDX.Direct2D1.GradientStopCollection)的方法,所以我只是简单地处理和为用户调整渐变停止偏移的每一帧重新创建渐变。

但是,我注意到如果我调整渐变停止足够多次(即,将其拖动 10 秒,同时线性渐变每秒重新创建 60 次),然后我注意到我的应用程序的内存使用量会激增无拘无束。我确定我在梯度上调用 Dispose:

private void RecreateBrush()
{

    var old = (SharpDX.Direct2D1.LinearGradientBrush)NativeBrush;
    NativeBrushLock.EnterWriteLock();

    NativeBrush = new SharpDX.Direct2D1.LinearGradientBrush(
        Target,
        new LinearGradientBrushProperties
        {
            StartPoint = new RawVector2(StartX, StartY),
            EndPoint = new RawVector2(EndX, EndY)
        },
        ConvertStops());

    old.GradientStopCollection.Dispose();
    old.Dispose();

    NativeBrushLock.ExitWriteLock();
}

但是我的内存使用量还在不断增加。使用 dotMemory 分析应用程序表明内存增加都是非托管内存,因此我开始深入挖掘,并使用 DebugDiag 2.0 分析我的应用程序。 DebugDiag 的分析指出 d3d11!NOutermost::CDevice::CreateLayeredChild+15f 是分配所有内存的罪魁祸首,"Leak Probability" 为 100%。

这是什么意思,如何消除内存泄漏?据我所知,我正在处理我正在创建的所有资源(ID2D1LinearGradientID2D1GradientStopCollection)。

这是我从 WinDbg 收集的堆栈跟踪:

d3d11!NOutermost::CDevice::CreateLayeredChild d3d11!CDevice::CreateTexture2D_Worker+0x47e d3d11!CDevice::CreateTexture2D+0xbf d2d1!CD3DDeviceCommon::CreateTexture+0x4c d2d1!CD3DSurface::Create+0xe1 d2d1!D2DGradientStopCollection::EnsureRealizedForBrushType+0x239 d2d1!D2DGradientBrush::SetGradientStopCollectionInternal+0x85 d2d1!D2DLinearGradientBrush::Create+0x8c d2d1!DrawingContext::CreateLinearGradientBrush+0xce d2d1!D2DDeviceContextBase::CreateLinearGradientBrush+0xe7 [Managed to Native Transition] SharpDX.Direct2D1.RenderTarget.CreateLinearGradientBrush(SharpDX.Direct2D1.LinearGradientBrushProperties, System.Nullable`1, SharpDX.Direct2D1.GradientStopCollection, SharpDX.Direct2D1.LinearGradientBrush) SharpDX.Direct2D1.LinearGradientBrush..ctor(SharpDX.Direct2D1.RenderTarget, SharpDX.Direct2D1.LinearGradientBrushProperties, SharpDX.Direct2D1.GradientStopCollection) MyApp.Direct2D.LinearGradientBrush.RecreateBrush() MyApp.Direct2D.LinearGradientBrush.OnStopsChanged(System.Object, System.Collections.Specialized.NotifyCollectionChangedEventArgs)

如您所见,我确保同时处理画笔和渐变停止集合(删除画笔后似乎继续存在)。但是,我的应用程序的本机内存使用量继续稳步增加,无法被 CLR 垃圾收集器回收。

错误出在我创建渐变停止集合时:ConvertStops() 产生 SharpDX.Direct2D1.GradientStopCollection。但是,我需要确保在调用 LinearGradientBrush 的构造函数后处理该渐变停止集合,因为虽然返回的 LinearGradientBrush 有一个 属性 GradientStopCollection 表示 pointer指向相同的值,不是同一个.NETinstance。因此,通过从 LinearGradientBrush.GradientStopCollection 中检索它来处理它不会将引用计数减少到 0,并且数据继续存在。

修改后的 RecreateBrush 方法如下所示:

private void RecreateBrush()
{
    var old = (SharpDX.Direct2D1.LinearGradientBrush) NativeBrush;

    NativeBrushLock.EnterWriteLock();

    using (var nativeStops = ConvertStops())
        NativeBrush = new SharpDX.Direct2D1.LinearGradientBrush(
            Target,
            new LinearGradientBrushProperties
            {
                StartPoint = new RawVector2(StartX, StartY),
                EndPoint = new RawVector2(EndX, EndY)
            },
            nativeStops);

    old.Dispose();

    NativeBrushLock.ExitWriteLock();
}