8

dotnet 读 WPF 源代码笔记 SafeMILHandleMemoryPressure 的作用

 3 years ago
source link: https://lindexi.gitee.io/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
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
dotnet 读 WPF 源代码笔记 SafeMILHandleMemoryPressure 的作用

本文来告诉大家在 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订阅博客,或者前往 CSDN 关注我的主页

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

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

以下是广告时间

推荐关注 Edi.Wang 的公众号
lindexi%2F201985113622445

欢迎进入 Eleven 老师组建的 .NET 社区
lindexi%2F20209121930471745.jpg

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK