[.NET Core知識點回顧]-自動記憶體管理
自動記憶體管理是公共語言運行時在託管執行過程中提供的服務之一。公共語言運行時的垃圾回收器為應用程式管理記憶體
的分配和釋放。對開發人員而言,在開發託管應用程式時不必編寫執行記憶體管理任務程式碼。
分配記憶體
初始化新進程時,運行時會為進程保留一個連續的地址空間區域。這個保留的地址空間被稱為託管堆。託管堆維護著一個指針,用它指向堆中分配的下一個對象的地址。初始時,該指針設置為指向託管堆的基礎地址。託管堆上包含所有的引用類型。應用程式在創建第一個引用類型時,將為託管堆的基址中的類型分配記憶體。應用程式創建下一個對象時,垃圾回收器在緊接第一個對象後面的地址空間內為它分配記憶體。只要地址空間可用,垃圾回收器就會繼續以這種方式為新對象分配空間。
在託管堆中分配記憶體要比非託管堆記憶體分配速度快。由於運行時通過為指針添加值來為對象分配記憶體,這個速度幾乎與堆棧中分配記憶體一樣快。另外由於連續分配的新對象在託管堆中是連續存儲,所以應用程式可以快速訪問這些對象。
釋放記憶體
垃圾回收器的優化引擎根據所執行的分配決定執行回收的最佳時間。垃圾回收器在執行回收時,會釋放應用程式不再使用的對象的記憶體。它通過檢查應用程式的根來確定不再使用的對象。每個應用程式都有一組根。每個根或者引用託管堆中的對象設置為空。應用程式的根包含執行緒堆棧上的靜態欄位、局部變數和參數以及CPU暫存器。垃圾回收器可以訪問由實時編輯器(JIT)和運行時維護的活動根的列表。垃圾回收器對照此列表檢查應用程式的根,並在此過程中創建一個圖表,在其中包含所有可從這個根中訪問的對象。
不在該圖表中的對象無法從應用程式的根中訪問。垃圾回收器會考慮無法訪問的對象垃圾,並釋放為他們分配記憶體。在回收中,垃圾回收器檢查託管堆,查找無法訪問對象佔據的地址空間塊。發現無法訪問的對象時,它就使用複製功能來壓縮記憶體中可以訪問的對象。釋放分配給不可訪問對象的地址空間。在壓縮了可訪問對象的記憶體後,垃圾回收器就會做出指針更正,一邊應用程式的根指向新地址中的對象。它會將託管堆指針定位至最後一個可訪問對象以後。
注意:垃圾回收器只要發現大量無法訪問的對象時,才會壓縮記憶體。如果託管堆中的所有對象均未被回收,則不需要壓縮記憶體。
為了改進性能,運行時(JIT)為單獨堆中的大型對象分配記憶體。垃圾回收器會自動釋放大型對象的記憶體。為了避免動記憶體中大型對象,不會壓縮此記憶體。
級別和性能
為了優化垃圾回收器的性能,將託管堆分為三代:第0代,第1代,第2代。運行時的垃圾回收演算法基於以下幾個普通原理,這些垃圾回收方案的原理已在軟體實驗中得到驗證。首先,壓縮託管堆的一部分記憶體要比壓縮整個託管堆速度塊。其次,比較新對象生存期比較短,而比較老的對象生存期比較長。最後,比較新的對象趨向於相互關聯,並同時由應用程式訪問。
運行時的垃圾回收器將新對象存儲在第0級中。在應用程式生存期的早期創建對象如果未被回收,則升級並存儲在第1級和第2級中。
實際上,垃圾回收器在第0級託管堆已滿時執行回收。如果應用程式在第0級託管堆已滿時嘗試新建對象,垃圾回收器將會發現第0級已沒有可分配給對象地址空間。垃圾回收器就執行回收操作。釋放第0級託管堆中的地址空間。垃圾回收器從第0級託管堆中的對象開始執行回收。
垃圾回收器執行第0級託管堆的回收後,會壓縮可訪問對象記憶體。然後垃圾回收器升級這些對象。並考慮第1級退管堆的這部分。因為未被回收的對象一般為比較長的生存期,所以將他們升級至更高級別。因此垃圾回收器每次回收第0級的託管堆,不會檢查第1級和第2級託管堆中的對象。
在執行第0級託管堆的首次回收並把可訪問的對象升級至第1級託管堆後,垃圾回收器將考慮第0級託管堆的其餘部分。它將繼續為第0級的託管堆中的新對象分配記憶體,直至第0級託管堆無法在分配地址後在執行另外一次的垃圾回收為止。此時,垃圾回收器的優化引擎會決定是否需要檢測比較舊的級別中的對象。如:垃圾回收器發現0級託管堆中釋放後的記憶體,不能再創建新的對象,垃圾回收器就會執行第1級託管堆的回收,然後再執行第2級託管堆的回收。如果這樣仍不能回收足夠的記憶體,垃圾回收器將執行第2,1和0級託管堆的回收。每次回收後,垃圾回收器都會壓縮第0級託管堆中的可訪問對象並將他們升級到第1級託管堆。第1級託管堆中未被回收的對象將會升級到第2級託管堆。由於垃圾回收器只支援三個級別,因此第2級託管堆中未被回收的對象繼續保留再第2級託管堆中,直到在將來的回收中確定題目為無法訪問為止。
為非託管資源釋放記憶體
對於應用程式創建的大多數對象,可以依賴垃圾回收器自動執行必要的記憶體管理任務。但是,非託管資源需要顯示清除。最常用的非託管堆資源類型時包裝作業系統資源的對象,如:文件句柄,窗口句柄或網路連接。雖然垃圾回收器可以跟蹤封裝非託管堆資源的託管對象的生存期,但卻無法具體了解如果清理資源。創建封裝非託管資源的對象時,建議在公共Dispose方法中提供必要的程式碼用以清理非託管堆資源。通過提供Dispose方法,對象的用戶可以在使用完對象後顯示釋放記憶體。使用封裝非託管資源的對象時,應在不使用的時候調用Dispose()方法釋放。