.NET 記憶體管理和垃圾回收
C/C++ 程式需要開發者手動配置設定和釋放記憶體,.Net程式則使用垃圾回收技術自動收集不再使用的記憶體。垃圾回收器(GC)使用引用
跟蹤占用記憶體的對象,如果對象被設定為null或已不在使用範圍,GC就會标志該對象為可回收,這樣GC就可以回收被這些對象占用的記憶體。
垃圾回收器(GC)使用Win32® VirtualAlloc() 接口為自己的堆配置設定記憶體,.Net托管堆是一個巨大連續的虛拟記憶體。GC先預留虛拟記憶體,當托管堆增長時則送出記憶體。GC跟蹤托管堆末尾可用的位址并把下一個配置設定請求放置在這個位址。這樣.Net 托管記憶體配置設定的托管堆是連續相鄰的。這極大提高配置設定時間,因為無需從記憶體塊連結表去搜尋。當程式運作一段時間後,由于對象的删除會在托管堆産生碎片,當垃圾回收發生時,GC通過直接記憶體拷貝來壓縮堆并填充這些碎片。
代(generations)
垃圾回收器(GC)通過把對象生命周期劃分為代的方式提高記憶體管理效率,當回收開始時,年輕一代的對象先被回收,如果這樣記憶體還不夠,接着老一代的會被回收。代的使用意味着GC每次隻和已配置設定對象集合的一個子集打交道。GC現在使用三個代,分别為gen0,gen1,gen2。配置設定對象開始屬于gen0,所有在gen0的對象在經曆一次回收後仍然存在的則提升到gen1, 在gen1的對象經曆gen0,gen1回收後還存在的則提升到gen2
逐漸地,最老一代的會填充最老的對象。這些老時代的對象會變得更穩定、所需更少的回收、更少的記憶體拷貝。
某個代的回收是當這個代的記憶體門檻值被突破後發生,在.Net 1.0, 最初的gen0,gen1,gen2的門檻值為256KB,2MB,10MB. 注意,GC會動态根據程式的記憶體配置設定模式來調整代的門檻值。超過85KB的對象會自動放置到大對象堆(LOH),LOH的處理下面單獨讨論
樹根(root)
GC使用對象引用判斷托管堆的某個記憶體塊是否可以被回收。與其他垃圾回收技術不同,.Net垃圾回收沒有為記憶體塊設定堆标志(heap flag)來訓示該記憶體塊是否可以被回收。 對每個程式,GC維護一個引用樹來跟蹤程式使用的對象。
GC認為一個對象可以被root如果這個對象至少有一個父親對象擁有一個引用指向它。每個.Net程式都有一套root的集合包括全局和靜态對象,以及關聯的線程堆棧和動态生成的對象。在開始垃圾回收前,GC從root開始往下建立所有變量引用的樹。GC同時建立一個所有活動對象的主清單,然後周遊托管堆查找那些不在這個活動對象清單裡的對象。
大對象堆(LOH)
.Net記憶體管理器把超過85K的配置設定都放到一個單獨的堆,稱為大對象堆。這個堆包含一系列虛拟記憶體塊獨立于主托管堆。為大對象使用單獨的堆使得主托管堆的垃圾回收更有效率,因為回收需要移動記憶體,而移動大塊記憶體是很耗時的,而且大對象堆是從來不壓縮的。
例如,如果你為一個單獨的塊配置設定1MB記憶體,LOH則擴充到1MB,當你釋放這個對象,LOH不會釋放該記憶體塊,是以LOH還是1MB,如果你又配置設定500KB對象,新的記憶體塊就配置設定在那1MB内。在程序生命周期内,LOH會一直增長來容納所有大的記憶體塊配置設定,但從不會縮小即使對象已經釋放,即使發生垃圾回收。
以上翻譯節選自MSDN