1. gzyueqian
      18529173453
      首頁 > 新聞中心 > > 正文

      編寫對(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/ 瀏覽。

      免費(fèi)預(yù)約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 午夜福利色佬网站 | 在线观看片a免费观看岛国 亚洲综合在线区尤物 | 亚洲日韩欧美国产动漫第二区 | 一区三区四区国产 | 先锋资源不卡在线视频 | 亚州AV一在线影视 |