ASP.NET堆和棧四之對托管和非托管資源的垃圾回收和內(nèi)存分配
".NET的堆和棧"系列:
在" ASP.NET堆和棧一之基本概念和值類型內(nèi)存分配"中,了解了"堆"和"棧"的基本概念,以及值類型的內(nèi)存分配。我們知道:當執(zhí)行一個方法的時候,值類型實例會在"棧"上分配內(nèi)存,而引用類型實例會在"堆"上分配內(nèi)存,當方法執(zhí)行完畢,"棧"上的實例由操作系統(tǒng)自動釋放,"堆"上的實例由.NET Framework的GC進行回收。
在" ASP.NET堆和棧二之值類型和引用類型參數(shù)傳遞和內(nèi)存分配"中,我們了解了值類型參數(shù)和引用類型參數(shù)在傳遞時的內(nèi)存分配情況。
在" ASP.NET堆和棧三之引用類型對象拷貝和內(nèi)存分配"中,我們了解了在拷貝引用類型對象時的內(nèi)存分配情況。
而本篇的重點要放在:對托管和非托管資源的垃圾回收、處理以及內(nèi)存分配情況。
什么樣的對象被GC認為是垃圾?
當托管堆中的對象不被任何其它對象所引用,這些對象將成為被釋放的垃圾對象等待被GC回收。
每個應用程序都有一組根指針,這些根指針是不會被回收的,是由JIT編譯器和CLR運行時維護的一個列表。主要包括:
- 全局/靜態(tài)指針:指向全局或局部靜態(tài)變量
- 棧指針:指向應用程序線程所需要的那部分棧上空間
- 寄存器指針:指向托管堆所需要的那部分CPU中的內(nèi)存地址
以上,假設(shè)托管堆中有5個對象,1和5被跟指針引用,3依賴1,那么在這組托管堆對象中,2和4被回收后變成如下:
當運行時有新的引用對象產(chǎn)生,將會被放到托管堆中這組對象的最上面。
GC如何回收?
GC對托管堆中對象的回收
GC采用一定的算法在托管堆中遍歷所有對象,最終形成一個可達對象和不可達對象,不可達對象將被回收。
GC對非托管堆中對象的回收、處理
對資源的回收
比如文件、數(shù)據(jù)庫鏈接、網(wǎng)絡(luò)鏈接等,這些不再托管堆中的對象如何被回收呢?
1、通過析構(gòu)函數(shù)回收
public class Sample{ //析構(gòu)函數(shù) ~Sample() { }}
在托管堆中,那些帶有析構(gòu)函數(shù)的實例,將被放置到"Finalization Queue"中。
對于那些不被任何其它對象所引用,如果沒有析構(gòu)函數(shù),比如2,將被直接回收,如果有析構(gòu)函數(shù),例如4,會被放到"Freachable Queue"中,等待GC實施下一輪回收。
當為一個類添加析構(gòu)函數(shù)后,為GC增加了額外的工作,代價是比較昂貴的,更現(xiàn)實的做法是讓類來實現(xiàn)IDisposable接口。
2、通過實現(xiàn)IDisposable接口回收
首先讓一個類實現(xiàn)IDisposable接口。
public class ResourceClass : IDisposable{ public void Dispose() {//TODO:實現(xiàn)回收邏輯 }}
在應用程序中調(diào)用如下實施回收。
using(ResourceClass re = new ResourceClass()){ }
對靜態(tài)值類型變量的處理
class Counter{ private static int s_Number = 0; public static int GetNextNumber() {int newNumber = s_Number;// DO SOME STUFFnewNumber += 1;s_Number = newNumber;return newNumber; }}
如上,當方法有方法處理靜態(tài)字段就需要注意了,2個線程同時調(diào)用GetNextNumber()會得到相同的結(jié)果,而我們的本意是:每調(diào)用一次方法,靜態(tài)字段s_Number自增1。
我們可以在處理邏輯塊中加鎖,每次只允許一個線程執(zhí)行。
class Counter{ private static int s_Number = 0; public static int GetNextNumber() {lock (typeof(Counter)){ int newNumber = s_Number; // DO SOME STUFF newNumber += 1; s_Number = newNumber; return newNumber;} }}
對靜態(tài)引用類型變量的處理
class Olympics{ public static Collection<Runner> TryoutRunners;} class Runner{ private string _fileName; private FileStream _fStream; public void GetStats() {FileInfo fInfo = new FileInfo(_fileName);_fStream = _fileName.OpenRead(); }}
以上,在GetStats()方法中,由于沒有對FileStream及時關(guān)閉,如果Olympics恰巧有10萬個Runner的集合,10萬Runner都執(zhí)行沒有關(guān)閉FileStream的Gettats()方法,這將是一場災難!
Singleton模式可以很好地避免上述問題,它保證了在任何時候,內(nèi)存中只存在某個類的一個實例。
public class Earth{ private static Earth _instance = new Earth(); private Earth(){} public static Earth GetInstance(){return _instance;}}
以上,單例模式的必要構(gòu)成要素包括:
1、私有靜態(tài)引用類型變量
2、私有構(gòu)造函數(shù)
3、獲取類實例的靜態(tài)方法
GC何時回收?
GC會周期性地執(zhí)行垃圾回收、內(nèi)存清理工作,以下情況會啟動GC:
- 托管堆內(nèi)存不足溢出時
- 調(diào)用GC.Collect()方法強制執(zhí)行垃圾回收
- Windows報告內(nèi)存不足
- CLR卸載AppDomain
GC回收之后,又執(zhí)行哪些操作?
GC在垃圾回收之后,托管堆上將出現(xiàn)多個被收集對象的"空洞",為了避免托管堆的內(nèi)存碎片,會重新分配內(nèi)存、壓縮托管堆。
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接
相關(guān)文章:
1. ASP.NET MVC實現(xiàn)樹形導航菜單2. ASP.NET Core按用戶等級授權(quán)的方法3. 使用HttpClient消費ASP.NET Web API服務案例4. 使用HttpClient增刪改查ASP.NET Web API服務5. ASP.NET Core實現(xiàn)中間件的幾種方式6. ASP.NET Core 依賴注入生命周期示例詳解7. asp.net core項目授權(quán)流程詳解8. ASP.NET MVC使用Session會話保持表單狀態(tài)9. ASP.NET MVC實現(xiàn)橫向展示購物車10. ASP.NET MVC限制同一個IP地址單位時間間隔內(nèi)的請求次數(shù)
