編寫對(duì)GC友好,又不泄漏的代碼
更新時(shí)間: 2007-06-04 10:12:34來源: 粵嵌教育瀏覽量:1275
1.使用更多生命周期短的、小的、不改變指向(immutable)的對(duì)象,編寫清晰的代碼。
出于懶惰也好,樸素的節(jié)儉意識(shí)也好,我們都習(xí)慣對(duì)一個(gè)變量重用再重用。但是....
Java的垃圾收集器喜歡短生命周期的對(duì)象,對(duì)象如果在新生代內(nèi),在垃圾收集發(fā)生前就死掉了,垃圾收集器就什么都不用做了。
現(xiàn)代JVM構(gòu)建一個(gè)新對(duì)象只需要10個(gè)本地CPU指令,并不弱于C/C++。 (但垃圾收集沒有壓縮算法時(shí)會(huì)稍慢,更頻繁的New對(duì)象也導(dǎo)致更頻繁的GC)。大對(duì)象的分配效率更低,而且對(duì)非壓縮算法的垃圾收集器,更容易造成碎片。
對(duì)象重用增加了代碼的復(fù)雜度,降低了可讀性。所以有標(biāo)題的呼吁,比如不要害怕為中間結(jié)果分配小對(duì)象。但編程習(xí)慣的改變也不是一朝一夕的事情。
2.將用完的對(duì)象設(shè)為NULL其實(shí)沒什么作用。
貌似很酷的把對(duì)象主動(dòng)設(shè)為Null 的"好習(xí)慣"其實(shí)沒什么用,JIT Compiler會(huì)自動(dòng)分析local變量的生命周期。只有一個(gè)例外情況,就是String[1024] foo 這種赤裸裸的數(shù)組,你需要主動(dòng)的foo[100]=null釋放第100號(hào)元素,所以還是直接用ArrayList這些標(biāo)準(zhǔn)庫(kù)算了。
3.避免顯式GC--System.gc()。
大家都知道System.gc()不好,full-gc浪費(fèi)巨大,gc的時(shí)機(jī)把握不一定對(duì)等等,甚至有-XX:+DisableExplicitGC的JVM參數(shù)來禁止它。
哈哈,但我還不會(huì)用System.gc()呢,不怕不怕。真的不怕嗎?
先用FindBugs 查一下所用到的全部第三方類庫(kù)吧...至少RMI 就會(huì)老實(shí)不客氣的執(zhí)行System.gc()來實(shí)現(xiàn)分布式GC算法。但我也不會(huì)用RMI啊。那EJB呢,EJB可是建在RMI上的.... 如果無可避免,用-sun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 (單位為微妙) 增大大GC的間隔(原默認(rèn)值為1分鐘),-X:+ExplicitGCInvokesConcurrent 讓System.gc() 也CMS并發(fā)執(zhí)行。
4.繼續(xù)千夫所指的finalize()
大家也都知道finalize()不好,分配代價(jià)昂貴,釋放代價(jià)更昂貴(要多走一個(gè)循環(huán),而且他們死得慢,和他們相關(guān)聯(lián)的對(duì)象也跟著死得慢了),又不確定能否被調(diào)用(JVM開始關(guān)閉時(shí),就不會(huì)再進(jìn)行垃圾收集),又不確定何時(shí)被調(diào)用(GC時(shí)間不定,即使system.gc()也只是提醒而不是強(qiáng)迫GC,又不確定以什么樣的順序調(diào)用,所以finalize不是C++的析構(gòu)函數(shù),也不像C++的析構(gòu)函數(shù)。
我們都知道啊,所以我從來都沒使用。都是在顯式的維護(hù)那些外部資源,比如在finally{}里釋放。
5.WeakReference/SoftReference
這是個(gè)平時(shí)不怎么會(huì)搭理,偶然知道了又覺得有用的Java特征。大家都知道Java里所有對(duì)象除int等基本類型外,都是Pass by Reference的指針,實(shí)例只要被一個(gè)對(duì)象連著,就不會(huì)被收集。而WeakReference就是真正意義上的C++指針,只是單純的指向一個(gè)對(duì)象,而不會(huì)影響對(duì)象的引用計(jì)數(shù)。而SoftReference更特別,在內(nèi)存足夠時(shí),對(duì)象會(huì)因?yàn)镾oftReference的存在而不被收集,但內(nèi)存不足時(shí),對(duì)象就還是會(huì)被收集,怎么看都是做簡(jiǎn)單緩存的料子。代碼如下:
Foo foo = new Foo();
SoftReference sr= new SoftReference(foo);
Foo bar = sr.get();
如果foo已被垃圾收集,sr.get()會(huì)返回Null;
另外還有一個(gè)ReferenceQueue的機(jī)制,使得對(duì)象被回收時(shí)能獲得通知,比finalize()完全不知道GC何時(shí)會(huì)執(zhí)行要聰明的多。
ReferenceQueue rq = new ReferenceQueue();
ref = new WeakReference(foo, rq);
WeakReference cleaned = rq.pool();
cleaned就是剛剛被GC掉的WeakReference。
6.內(nèi)存泄漏
java 不是有垃圾收集器了嗎?怎么還泄漏啊,唬我啊??嗯,此泄漏非比泄漏。C/C++的泄漏,是對(duì)象已不可到達(dá),而內(nèi)存又沒有回收,真正的內(nèi)存黑洞。而Java的泄漏,則是因?yàn)楦鞣N原因,對(duì)象對(duì)應(yīng)用已經(jīng)無用,但一直被持有,一直可到達(dá)。總結(jié)原因無外乎幾方面:
被生命周期極長(zhǎng)的集合類不當(dāng)持有,號(hào)稱是Java內(nèi)存泄漏的首因。
這些集合類的生命周期通常極長(zhǎng),而且是一個(gè)輔助管理性質(zhì)的對(duì)象,在一個(gè)業(yè)務(wù)事務(wù)運(yùn)行完后,如果沒有將某個(gè)業(yè)務(wù)對(duì)象主動(dòng)的從中清除的話,這個(gè)集合就會(huì)吃越來越多內(nèi)存,可以用WeakReference,如WeakHashMap,使得它持有的對(duì)象不增加對(duì)象的引用數(shù)。
Scope定義不對(duì),這個(gè)很簡(jiǎn)單了,方法的局部變量定義成類的變量,類的靜態(tài)變量等。
異常時(shí)沒有加finally{}來釋放某些資源,JDBC時(shí)代也是很普遍的事情。
另外一些我了解不深的原因,如:Swing里的Listener沒有顯式remove;內(nèi)部類持有外部對(duì)象的隱式引用;Finalizers造成關(guān)聯(lián)對(duì)象沒有被及時(shí)清空等。
內(nèi)存泄漏的檢測(cè)
有不少工具輔助做這個(gè)事情的,如果手上一個(gè)工具也沒有,可以用JDK自帶的小工具:
看看誰占滿了Heap?
用jmap可以顯示運(yùn)行程序中對(duì)象的類型,個(gè)數(shù)與所占的大小
先用jps 找到進(jìn)程號(hào),然后jmap -histo pid 顯示或 jmap -dump:file=heap_file_name pid 導(dǎo)出heap文件
為什么這些對(duì)象仍然可以到達(dá)?
用jhat(Java Heap Analysis Tool) 分析剛才導(dǎo)出的heap文件。
先jhat heap_file_name,然后打開瀏覽器http://localhost:7000/ 瀏覽。
粵嵌動(dòng)態(tài)
推薦閱讀
- ·Linux字符設(shè)備驅(qū)動(dòng)框架解析:file_operations的核心作用與實(shí)現(xiàn)
- ·廣東朝歌數(shù)碼科技股份有限公司專場(chǎng)招聘會(huì)
- ·深化產(chǎn)教融合,共筑技能人才培養(yǎng)新生態(tài) —— 廣州華立學(xué)院到訪粵嵌從化校區(qū)為深化產(chǎn)教
- ·校企合作新突破 | 粵嵌科技與三亞學(xué)院共探產(chǎn)教融合新路徑
- ·粵嵌科技入選國(guó)家級(jí)職業(yè)數(shù)字展館聯(lián)合建設(shè)單位,賦能計(jì)算機(jī)程序設(shè)計(jì)員高技能人才培養(yǎng)
- ·嵌入式實(shí)時(shí)操作系統(tǒng)的性能優(yōu)化與實(shí)現(xiàn)路徑
- ·校企攜手賦能教育!粵嵌科技助力海南科技職業(yè)大學(xué)探索 AGI 時(shí)代教學(xué)新范式
- ·嵌入式系統(tǒng)中的低功耗設(shè)計(jì)策略與實(shí)現(xiàn)路徑
- ·深圳市軒宇軟件開發(fā)有限公司專場(chǎng)招聘會(huì)
- ·嵌入式系統(tǒng)中的代碼空間優(yōu)化:策略與實(shí)踐