本文来告诉大家在 WPF 里面的 SafeMILHandleMemoryPressure 类的作用。这是一个 internal 不开放的类,是在 WPF 中和 Dx 等模块调用使用的,用途就是辅助 GC 统计当前内存情况,用来在内存不够的时候触发回收

这个类放在 src\Microsoft.DotNet.Wpf\src\PresentationCore\System\Windows\Media\SafeMILHandleMemoryPressure.cs 文件,核心调用是通过 GC.AddMemoryPressure(Int64) 方法告诉 GC 当前非托管部分占用了多少内存

根据 GC.AddMemoryPressure(Int64) 官方文档 的说法,这个 AddMemoryPressure 需要和 RemoveMemoryPressure 成对使用,在使用的时候必须由业务方成对调用,否则将会影响 GC 的效率

为什么需要有 GC.AddMemoryPressure 这个方法?原因是假定咱的所有代码都是托管的清真的代码,那么 GC 是能统计当前占用了多少的内存的。但如果咱调用了一些非托管部分,这些模块也申请了内存,此时的 GC 是不了解当前使用到多少内存的,属于这个非托管模块用的内存是多少。通过 GC.AddMemoryPressure 这个方法可以告诉 GC 当前这个非托管模块使用到多少内存了

而 GC 的清理是需要根据当前内存占用量决定的,假定现在内存多的是,而且进程也没有用多少内存,那么 GC 将不会进行全清理。但如果当前进程用到了大量的内存了,那么 GC 也许就需要考虑来一次完全内存回收了。上面说的内存完全回收大概可以理解为回收到二代同时压缩内存,更多内存细节请看伟民哥翻译的 .NET内存管理宝典 - 提高代码质量、性能和可扩展性 这本书

那如果我只是调用了 GC.AddMemoryPressure 但没有调用 RemoveMemoryPressure 方法会如何?此时的 GC 将会以为内存里面有这些模块占用了内存,而且这些模块也没有释放

为了能在 WPF 里面更好管理内存,同时成对调用 GC.AddMemoryPressure 和 RemoveMemoryPressure 方法,而且是准确在非托管释放的时候调用 RemoveMemoryPressure 方法,就封装了 SafeMILHandleMemoryPressure 类

在 SafeMILHandleMemoryPressure 的构造函数里面,将会传入当前非托管模块使用到的内存量

        internal SafeMILHandleMemoryPressure(long gcPressure)
        {
            _gcPressure = gcPressure;
            _refCount = 0;

            
            // Removed WPF specific GC algorithm and all bitmap allocations/deallocations
            // are now tracked with GC.Add/RemoveMemoryPressure.
            GC.AddMemoryPressure(_gcPressure);
        }

接着跟随非托管的指针引用添加或减少引用,相当于自己实现了引用计算。在引用数量为 零 的时候,调用 RemoveMemoryPressure 方法告诉 GC 非托管没有占用资源

        internal void AddRef()
        {
            Interlocked.Increment(ref _refCount);
        }

        internal void Release()
        {
            if (Interlocked.Decrement(ref _refCount) == 0)
            {
                
                // Removed WPF specific GC algorithm and all bitmap allocations/deallocations
                // are now tracked with GC.Add/RemoveMemoryPressure.
                GC.RemoveMemoryPressure(_gcPressure);
                _gcPressure = 0;
            }
        }

        // Estimated size in bytes of the unmanaged memory
        private long _gcPressure;

        //
        // SafeMILHandleMemoryPressure does its own ref counting in managed code, because the
        // associated memory pressure should be removed when there are no more managed
        // references to the unmanaged resource. There can still be references to it from
        // unmanaged code elsewhere, but that should not prevent the memory pressure from being
        // released.
        //
        private int _refCount;

当前这个类只是在和 MIL 调用这里使用,但设计是通用的

GC.AddMemoryPressure(Int64) Method (System)


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-SafeMILHandleMemoryPressure-%E7%9A%84%E4%BD%9C%E7%94%A8.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者收藏我的博客导航

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

微软最具价值专家


无盈利,不卖课,做纯粹的技术博客

以下是广告时间

推荐关注 Edi.Wang 的公众号

欢迎进入 Eleven 老师组建的 .NET 社区

以上广告全是友情推广,无盈利