.net的gc垃圾回收原理及實現(xiàn)
一、先了解下必備的知識前提
內(nèi)存中的托管與非托管,可簡單理解為:
托管:可借助gc從內(nèi)存中釋放的數(shù)據(jù)對象(以下要描述的內(nèi)容點)
非托管:必須手工借助dispose釋放資源(實現(xiàn)自idisposable)的對象
內(nèi)存中有棧和堆的概念區(qū)分,僅簡單說明:
棧:先進(jìn)后出 的特點(這里不再詳細(xì)闡述)
堆:存放數(shù)據(jù)對象實例的內(nèi)存空間(以下要描述的內(nèi)容點)
二、.net gc的簡單描述
gc垃圾回收是對于內(nèi)存堆的處理過程。
當(dāng)一個應(yīng)用程序進(jìn)程創(chuàng)建時,會為此應(yīng)用程序在物理內(nèi)存堆中分配一塊虛擬的連續(xù)性內(nèi)存空間,以供應(yīng)用程序后續(xù)運行時存放產(chǎn)生的數(shù)據(jù)對象實例。
gc是一個獨立的進(jìn)程,用來自動維護(hù)管理內(nèi)存堆中的空間分配和釋放。它通過一個或多個線程進(jìn)行垃圾回收,默認(rèn)啟用后臺線程垃圾回收。(關(guān)于前臺線程與后臺線程,可參考其它)
三、.net平臺的gc垃圾回收,什么時候會被觸發(fā)呢?
1、當(dāng)被分配的堆中虛擬內(nèi)存空間不夠用時,系統(tǒng)會自動 回收/壓縮/擴大 被分配的虛擬內(nèi)存塊,以適應(yīng)新產(chǎn)生的數(shù)據(jù)對象存儲。
2、當(dāng)整個物理內(nèi)存不夠用時,系統(tǒng)會自動 回收/壓縮 各個進(jìn)程占用的內(nèi)存空間,以適應(yīng)新產(chǎn)生的數(shù)據(jù)對象存儲。
3、當(dāng)應(yīng)用程序中手動觸發(fā)gc回收時,gc按照手動指定的方式進(jìn)行垃圾回收。
四、從作用域上 去理解堆中的代
先這樣去理解吧
假設(shè)一個實例變量聲明時的作用域較大,那它就不會馬上被回收,因為作用域大的因素,有可能后續(xù)程序時常還會被用到。
假設(shè)一個實例變量聲明時的作用域較小,那它就有可能被優(yōu)先回收,因為生存周期較短,過了作用域范圍,此變量不會再被使用。
假設(shè)一個靜態(tài)的或全局的作用域變量,那它通常不會被回收,因為這樣的全局聲明會在任意代碼段長期被使用。
所以,為了更好的回收,堆中將各數(shù)據(jù)對象實例歸納為:0代、1代、2代
0代:臨時或最新創(chuàng)建的數(shù)據(jù)對象實例。最常被回收的對象實例。
1代:一段時間內(nèi)再次使用的數(shù)據(jù)對象實例,生命周期較長的數(shù)據(jù)對象實例。較少被回收的對象實例。
2代:常住內(nèi)存的對象實例,如:靜態(tài)類型,全局作用域等的對象實例。通常為應(yīng)用程序退出后回收。
五、堆中對象 在代之間的轉(zhuǎn)移:幸存者的提升
應(yīng)用程序持續(xù)運行中,
新創(chuàng)建的對象首先被放在0代中,當(dāng)運行一段時間后,有些變量超出了自己所在的作用域,不會再被使用,會被gc清理;
由于有些變量作用域大,當(dāng)前還未超出自己所在的作用域,接下來可能還會被使用,所以gc不會清理;
0代中,有些數(shù)據(jù)對象實例會被gc清理,有些數(shù)據(jù)實例對象未被gc清理,那么,未被gc清理的數(shù)據(jù)對象實例,我們稱它為幸存者。
此時,0代中的幸存者會被轉(zhuǎn)移到1代中(想想上面提到1代存放的是哪類對象實例...);
那么,以此類推,長期/處處被使用的對象實例,就會從1代中轉(zhuǎn)移到2代中;
因此,2代中存放的通常為靜態(tài)或全局作用域或長期被使用到的對象實例。
六、gc是如何去確定要清理的對象實例?
gc在堆中生成各對象間的結(jié)構(gòu)圖,作為回收對象的依據(jù),找出非活動的對象。
所有數(shù)據(jù)對象實例之間的關(guān)聯(lián)引用關(guān)系,都會生成一個完整的結(jié)構(gòu)圖,一些不在結(jié)構(gòu)圖中的 或超出所在作用域的 或不再被繼續(xù)使用的對象實例,被稱為非活動對象。被視為gc要清理的對象。
準(zhǔn)確的說:
- 堆棧根
- 垃圾回收句柄
- 靜態(tài)數(shù)據(jù)
七、手動gc垃圾回收
在某些不常見的情況下,強制回收可提高應(yīng)用程序的性能。在此,可使用 gc.collect 方法強制執(zhí)行垃圾回收,從而誘導(dǎo)垃圾回收。
注意,是誘導(dǎo),而不是即刻回收。
為了考慮到應(yīng)用程序當(dāng)前的穩(wěn)定運行,執(zhí)行g(shù)c.collect并不一定馬上產(chǎn)生效果,這里僅僅是一個觸發(fā),會去收集將要回收的對象,回收動作會在未來某個合適的時間段進(jìn)行。(當(dāng)然,也可以強制阻塞式回收,這里略過)
(思考一下:無用的實例=null,是否告知gc為可回收的對象?再gc.collect()后的效果。)
關(guān)于 gc.collect 方法的參數(shù),會用到上面提到的概念及場景:
- 對指定的代進(jìn)行回收
- 指定回收次數(shù)
- 強制回收 或 擇機回收
- 阻塞式回收 或 后臺線程回收
- 壓縮 或 清理
(阻塞式回收方式:都先停一停,先讓我回收完)
當(dāng)然,通常建議:0代,擇機,后臺回收(阻塞式風(fēng)險太大,通常選擇擇機方式,具體自我考量)
八、內(nèi)存堆中的弱引用
當(dāng)應(yīng)用程序正在執(zhí)行使用的對象,gc是不可能回收的,那么,就認(rèn)為應(yīng)用程序?qū)υ搶ο缶哂袕娨谩?/p>
強引用:應(yīng)用程序正在使用的對象實例,不能被gc回收。
弱引用:應(yīng)用程序暫時沒使用的對象實例,暫時可被gc定義為可回收的實例,在回收之前,也可被應(yīng)用程序再次使用后變?yōu)閺娨谩?/p>
假設(shè)一個對象實例被gc清理后,后續(xù)又被再次用到的場景,就會重新創(chuàng)建對象實例,那如果這個對象實例又比較大,這樣的頻繁創(chuàng)建... ...
當(dāng)然還有優(yōu)化的空間,所以,弱引用優(yōu)化了以上場景。
弱引用的優(yōu)點:對于頻繁創(chuàng)建的大實例,弱類型可以做到一次創(chuàng)建多次使用,避免大對象實例多次創(chuàng)建的性能消耗。
(對于小對象使用弱類型,所帶來的對對象管理上的性能消耗,是否值得)
若要對某對象建立弱引用,使用要跟蹤的對象實例創(chuàng)建 weakreference。 然后將 target 屬性設(shè)置為該對象,將該對象的原始引用設(shè)置為 null。(參考官方文檔)
也就是說:我們可以自定義控制哪些對象實例要不要暫時不被gc垃圾回收。
九、多應(yīng)用共享內(nèi)存時的垃圾回收
當(dāng)多個應(yīng)用程序在一臺主機同時運行時,對內(nèi)存空間大小的分配,建議是靈活可變的,以達(dá)到各應(yīng)用程序?qū)?nèi)存利用的平衡及穩(wěn)定性。
如果啟用 gctrimcommitonlowmemory 設(shè)置,垃圾回收器會計算系統(tǒng)內(nèi)存負(fù)載,并在負(fù)載達(dá)到 90% 時進(jìn)入修整模式。除非負(fù)載下降到不到 85%,否則會一直處于修整模式。
如果條件允許,垃圾回收器可以決定 gctrimcommitonlowmemory 設(shè)置對當(dāng)前應(yīng)用沒有幫助并忽略它。
如下啟用 gctrimcommitonlowmemory 設(shè)置
<?xml version="1.0" encoding="utf-8"?> <configuration> <runtime> <gctrimcommitonlowmemory enabled="true"/> </runtime> </configuration>
關(guān)于.net的gc垃圾回收原理及實現(xiàn)的文章就介紹至此,更多相關(guān).net gc垃圾回收 內(nèi)容請搜索碩編程以前的文章,希望大家多多支持碩編程!