1. 程式人生 > 其它 >dotnet 讀 WPF 原始碼筆記 SafeMILHandleMemoryPressure 的作用

dotnet 讀 WPF 原始碼筆記 SafeMILHandleMemoryPressure 的作用

技術標籤:WPF原始碼WPFc#dotnetC#WPF

本文來告訴大家在 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/ 歡迎大家訪問,裡面有很多新的部落格。只有在我看到部落格寫成熟之後才會放在csdn或部落格園,但是一旦釋出了就不再更新

如果在部落格看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎大家加入

如有不方便在部落格評論的問題,可以加我 QQ 2844808902 交流

知識共享許可協議
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含連結:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡。