CLR内存管理
自動內(nèi)存管理是公共語言運(yùn)行時在托管執(zhí)行過程過程中提供的服務(wù)之一。 公共語言運(yùn)行時的垃圾回收器為應(yīng)用程序管理內(nèi)存的分配和釋放。 對開發(fā)人員而言,這就意味著在開發(fā)托管應(yīng)用程序時不必編寫執(zhí)行內(nèi)存管理任務(wù)的代碼。 自動內(nèi)存管理可解決常見問題,例如,忘記釋放對象并導(dǎo)致內(nèi)存泄漏,或嘗試訪問已釋放對象的內(nèi)存。 本節(jié)描述垃圾回收器如何分配和釋放內(nèi)存。
分配內(nèi)存初始化新進(jìn)程時,運(yùn)行時會為進(jìn)程保留一個連續(xù)的地址空間區(qū)域。 這個保留的地址空間被稱為托管堆。 托管堆維護(hù)著一個指針,用它指向?qū)⒃诙阎蟹峙涞南乱粋€對象的地址。 最初,該指針設(shè)置為指向托管堆的基址。 托管堆上部署了所有引用類型。 應(yīng)用程序創(chuàng)建第一個引用類型時,將為托管堆的基址中的類型分配內(nèi)存。 應(yīng)用程序創(chuàng)建下一個對象時,垃圾回收器在緊接第一個對象后面的地址空間內(nèi)為它分配內(nèi)存。 只要地址空間可用,垃圾回收器就會繼續(xù)以這種方式為新對象分配空間。
從托管堆中分配內(nèi)存要比非托管內(nèi)存分配速度快。 由于運(yùn)行時通過為指針添加值來為對象分配內(nèi)存,所以這幾乎和從堆棧中分配內(nèi)存一樣快。 另外,由于連續(xù)分配的新對象在托管堆中是連續(xù)存儲,所以應(yīng)用程序可以快速訪問這些對象。
釋放內(nèi)存垃圾回收器的優(yōu)化引擎根據(jù)所執(zhí)行的分配決定執(zhí)行回收的最佳時間。 垃圾回收器在執(zhí)行回收時,會釋放應(yīng)用程序不再使用的對象的內(nèi)存。 它通過檢查應(yīng)用程序的根來確定不再使用的對象。 每個應(yīng)用程序都有一組根。 每個根或者引用托管堆中的對象,或者設(shè)置為空。 應(yīng)用程序的根包含全局對象指針、靜態(tài)對象指針、線程堆棧中的局部變量和引用對象參數(shù)以及 CPU 寄存器。 垃圾回收器可以訪問由實時 (JIT) 編譯器和運(yùn)行時維護(hù)的活動根的列表。 垃圾回收器對照此列表檢查應(yīng)用程序的根,并在此過程中創(chuàng)建一個圖表,在其中包含所有可從這些根中訪問的對象。
不在該圖表中的對象將無法從應(yīng)用程序的根中訪問。 垃圾回收器會考慮無法訪問的對象垃圾,并釋放為它們分配的內(nèi)存。 在回收中,垃圾回收器檢查托管堆,查找無法訪問對象所占據(jù)的地址空間塊。 發(fā)現(xiàn)無法訪問的對象時,它就使用內(nèi)存復(fù)制功能來壓縮內(nèi)存中可以訪問的對象,釋放分配給不可訪問對象的地址空間塊。 在壓縮了可訪問對象的內(nèi)存后,垃圾回收器就會做出必要的指針更正,以便應(yīng)用程序的根指向新地址中的對象。 它還將托管堆指針定位至最后一個可訪問對象之后。 請注意,只有在回收發(fā)現(xiàn)大量的無法訪問的對象時,才會壓縮內(nèi)存。 如果托管堆中的所有對象均未被回收,則不需要壓縮內(nèi)存。
為了改進(jìn)性能,運(yùn)行時為單獨(dú)堆中的大型對象分配內(nèi)存。 垃圾回收器會自動釋放大型對象的內(nèi)存。 但是,為了避免移動內(nèi)存中的大型對象,不會壓縮此內(nèi)存。
級別和性能為優(yōu)化垃圾回收器的性能,將托管堆分為三代:第 0 代、第 1 代和第 2 代。 運(yùn)行時的垃圾回收算法基于以下幾個普遍原理,這些垃圾回收方案的原理已在計算機(jī)軟件業(yè)通過實驗得到了證實。 首先,壓縮托管堆的一部分內(nèi)存要比壓縮整個托管堆速度快。 其次,較新的對象生存期較短,而較舊的對象生存期則較長。 最后,較新的對象趨向于相互關(guān)聯(lián),并且大致同時由應(yīng)用程序訪問。
運(yùn)行時的垃圾回收器將新對象存儲在第 0 級中。 在應(yīng)用程序生存期的早期創(chuàng)建的對象如果未被回收,則被升級并存儲在第 1 級和第 2 級中。 本主題中稍后介紹了對象升級過程。 因為壓縮托管堆的一部分要比壓縮整個托管堆速度快,所以此方案允許垃圾回收器在每次執(zhí)行回收時釋放特定級別的內(nèi)存,而不是整個托管堆的內(nèi)存。
實際上,垃圾回收器在第 0 級托管堆已滿時執(zhí)行回收。 如果應(yīng)用程序在第 0 級托管堆已滿時嘗試新建對象,垃圾回收器將會發(fā)現(xiàn)第 0 級托管堆中沒有可分配給該對象的剩余地址空間。 垃圾回收器執(zhí)行回收,嘗試為對象釋放第 0 級托管堆中的地址空間。 垃圾回收器從檢查第 0 級托管堆中的對象(而不是托管堆中的所有對象)開始執(zhí)行回收。 這是最有效的途徑,因為新對象的生存期往往較短,并且期望在執(zhí)行回收時,應(yīng)用程序不再使用第 0 級托管堆中的許多對象。 另外,單獨(dú)回收第 0 級托管堆通常可以回收足夠的內(nèi)存,這樣,應(yīng)用程序便可以繼續(xù)創(chuàng)建新對象。
垃圾回收器執(zhí)行第 0 級托管堆的回收后,會壓縮可訪問對象的內(nèi)存,如本主題前面的釋放內(nèi)存中所述。 然后,垃圾回收器升級這些對象,并考慮第 1 級托管堆的這一部分。 因為未被回收的對象往往具有較長的生存期,所以將它們升級至更高的級別很有意義。 因此,垃圾回收器在每次執(zhí)行第 0 級托管堆的回收時,不必重新檢查第 1 級和第 2 級托管堆中的對象。
在執(zhí)行第 0 級托管堆的首次回收并把可訪問的對象升級至第 1 級托管堆后,垃圾回收器將考慮第 0 級托管堆的其余部分。 它將繼續(xù)為第 0 級托管堆中的新對象分配內(nèi)存,直至第 0 級托管堆已滿并需執(zhí)行另一回收為止。 這時,垃圾回收器的優(yōu)化引擎會決定是否需要檢查較舊的級別中的對象。 例如,如果第 0 級托管堆的回收沒有回收足夠的內(nèi)存,不能使應(yīng)用程序成功完成創(chuàng)建新對象的嘗試,垃圾回收器就會先執(zhí)行第 1 級托管堆的回收,然后再執(zhí)行第 2 級托管堆的回收。 如果這樣仍不能回收足夠的內(nèi)存,垃圾回收器將執(zhí)行第 2、1 和 0 級托管堆的回收。 每次回收后,垃圾回收器都會壓縮第 0 級托管堆中的可訪問對象并將它們升級至第 1 級托管堆。 第 1 級托管堆中未被回收的對象將會升級至第 2 級托管堆。 由于垃圾回收器只支持三個級別,因此第 2 級托管堆中未被回收的對象會繼續(xù)保留在第 2 級托管堆中,直到在將來的回收中確定它們?yōu)闊o法訪問為止。
為非托管資源釋放內(nèi)存對于應(yīng)用程序創(chuàng)建的大多數(shù)對象,可以依賴?yán)厥掌髯詣訄?zhí)行必要的內(nèi)存管理任務(wù)。 但是,非托管資源需要顯式清除。 最常用的非托管資源類型是包裝操作系統(tǒng)資源的對象,例如,文件句柄、窗口句柄或網(wǎng)絡(luò)連接。 雖然垃圾回收器可以跟蹤封裝非托管資源的托管對象的生存期,但卻無法具體了解如何清理資源。 創(chuàng)建封裝非托管資源的對象時,建議在公共 Dispose 方法中提供必要的代碼以清理非托管資源。 通過提供 Dispose 方法,對象的用戶可以在使用完對象后顯式釋放其內(nèi)存。 使用封裝非托管資源的對象時,應(yīng)該了解 Dispose 并在必要時調(diào)用它
總結(jié)
- 上一篇: .Net Framwork概述
- 下一篇: 【我的定义】