CLR内存管理
自動內存管理是公共語言運行時在托管執行過程過程中提供的服務之一。 公共語言運行時的垃圾回收器為應用程序管理內存的分配和釋放。 對開發人員而言,這就意味著在開發托管應用程序時不必編寫執行內存管理任務的代碼。 自動內存管理可解決常見問題,例如,忘記釋放對象并導致內存泄漏,或嘗試訪問已釋放對象的內存。 本節描述垃圾回收器如何分配和釋放內存。
分配內存初始化新進程時,運行時會為進程保留一個連續的地址空間區域。 這個保留的地址空間被稱為托管堆。 托管堆維護著一個指針,用它指向將在堆中分配的下一個對象的地址。 最初,該指針設置為指向托管堆的基址。 托管堆上部署了所有引用類型。 應用程序創建第一個引用類型時,將為托管堆的基址中的類型分配內存。 應用程序創建下一個對象時,垃圾回收器在緊接第一個對象后面的地址空間內為它分配內存。 只要地址空間可用,垃圾回收器就會繼續以這種方式為新對象分配空間。
從托管堆中分配內存要比非托管內存分配速度快。 由于運行時通過為指針添加值來為對象分配內存,所以這幾乎和從堆棧中分配內存一樣快。 另外,由于連續分配的新對象在托管堆中是連續存儲,所以應用程序可以快速訪問這些對象。
釋放內存垃圾回收器的優化引擎根據所執行的分配決定執行回收的最佳時間。 垃圾回收器在執行回收時,會釋放應用程序不再使用的對象的內存。 它通過檢查應用程序的根來確定不再使用的對象。 每個應用程序都有一組根。 每個根或者引用托管堆中的對象,或者設置為空。 應用程序的根包含全局對象指針、靜態對象指針、線程堆棧中的局部變量和引用對象參數以及 CPU 寄存器。 垃圾回收器可以訪問由實時 (JIT) 編譯器和運行時維護的活動根的列表。 垃圾回收器對照此列表檢查應用程序的根,并在此過程中創建一個圖表,在其中包含所有可從這些根中訪問的對象。
不在該圖表中的對象將無法從應用程序的根中訪問。 垃圾回收器會考慮無法訪問的對象垃圾,并釋放為它們分配的內存。 在回收中,垃圾回收器檢查托管堆,查找無法訪問對象所占據的地址空間塊。 發現無法訪問的對象時,它就使用內存復制功能來壓縮內存中可以訪問的對象,釋放分配給不可訪問對象的地址空間塊。 在壓縮了可訪問對象的內存后,垃圾回收器就會做出必要的指針更正,以便應用程序的根指向新地址中的對象。 它還將托管堆指針定位至最后一個可訪問對象之后。 請注意,只有在回收發現大量的無法訪問的對象時,才會壓縮內存。 如果托管堆中的所有對象均未被回收,則不需要壓縮內存。
為了改進性能,運行時為單獨堆中的大型對象分配內存。 垃圾回收器會自動釋放大型對象的內存。 但是,為了避免移動內存中的大型對象,不會壓縮此內存。
級別和性能為優化垃圾回收器的性能,將托管堆分為三代:第 0 代、第 1 代和第 2 代。 運行時的垃圾回收算法基于以下幾個普遍原理,這些垃圾回收方案的原理已在計算機軟件業通過實驗得到了證實。 首先,壓縮托管堆的一部分內存要比壓縮整個托管堆速度快。 其次,較新的對象生存期較短,而較舊的對象生存期則較長。 最后,較新的對象趨向于相互關聯,并且大致同時由應用程序訪問。
運行時的垃圾回收器將新對象存儲在第 0 級中。 在應用程序生存期的早期創建的對象如果未被回收,則被升級并存儲在第 1 級和第 2 級中。 本主題中稍后介紹了對象升級過程。 因為壓縮托管堆的一部分要比壓縮整個托管堆速度快,所以此方案允許垃圾回收器在每次執行回收時釋放特定級別的內存,而不是整個托管堆的內存。
實際上,垃圾回收器在第 0 級托管堆已滿時執行回收。 如果應用程序在第 0 級托管堆已滿時嘗試新建對象,垃圾回收器將會發現第 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 并在必要時調用它
總結
- 上一篇: .Net Framwork概述
- 下一篇: 【我的定义】