明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

對(duì)象引用是如何嚴(yán)重影響垃圾收集器的

[摘要]果您認(rèn)為 Java 游戲開(kāi)發(fā)人員是 Java 編程世界的一級(jí)方程式賽車手,那么您就會(huì)明白為什么他們會(huì)如此地重視程序的性能。 游戲開(kāi)發(fā)人員幾乎每天都要面對(duì)的性能問(wèn)題,往往超過(guò)了一般程序員考慮問(wèn)題的范圍。哪里可以找到這些特殊的開(kāi)發(fā)人員呢?Java 游戲社區(qū)就是一個(gè)好去處(參見(jiàn) 參考資料)。 雖然在這個(gè)...
果您認(rèn)為 Java 游戲開(kāi)發(fā)人員是 Java 編程世界的一級(jí)方程式賽車手,那么您就會(huì)明白為什么他們會(huì)如此地重視程序的性能。 游戲開(kāi)發(fā)人員幾乎每天都要面對(duì)的性能問(wèn)題,往往超過(guò)了一般程序員考慮問(wèn)題的范圍。哪里可以找到這些特殊的開(kāi)發(fā)人員呢?Java 游戲社區(qū)就是一個(gè)好去處(參見(jiàn) 參考資料)。 雖然在這個(gè)站點(diǎn)可能沒(méi)有很多關(guān)于服務(wù)器端的應(yīng)用,但是我們依然可以從中受益,看看這些“惜比特如金”的游戲開(kāi)發(fā)人員每天所面對(duì)的,我們往往能從中得到寶貴的經(jīng)驗(yàn)。讓我們開(kāi)始游戲吧!

  對(duì)象泄漏

  游戲程序員跟其他程序員一樣――他們也需要理解 Java 運(yùn)行時(shí)環(huán)境的一些微妙之處,比如垃圾收集。垃圾收集可能是使您感到難于理解的較難的概念之一, 因?yàn)樗⒉荒芸偸呛翢o(wú)遺漏地解決 Java 運(yùn)行時(shí)環(huán)境中堆管理的問(wèn)題。似乎有很多類似這樣的討論,它的開(kāi)頭或結(jié)尾寫著:“我的問(wèn)題是關(guān)于垃圾收集”。

  假如您正面遭遇內(nèi)存耗盡(out-of-memory)的錯(cuò)誤。于是您使用檢測(cè)工具想要找到問(wèn)題所在,但這是徒勞的。您很容易想到另外一個(gè)比較可信的原因:這是 Java 虛擬機(jī)堆管理的問(wèn)題,而不會(huì)認(rèn)為這是您自己的程序的緣故。但是,正如 Java 游戲社區(qū)的資深專家不止一次地解釋的,Java 虛擬機(jī)并不存在任何被證實(shí)的對(duì)象泄漏問(wèn)題。實(shí)踐證明,垃圾收集器一般能夠精確地判斷哪些對(duì)象可被收集,并且重新收回它們的內(nèi)存空間給 Java 虛擬機(jī)。所以,如果您遇到了內(nèi)存耗盡的錯(cuò)誤,那么這完全可能是由您的程序造成的,也就是說(shuō)您的程序中存在著“無(wú)意識(shí)的對(duì)象保留(unintentional object retention)”。

  內(nèi)存泄漏與無(wú)意識(shí)的對(duì)象保留

  內(nèi)存泄漏和無(wú)意識(shí)的對(duì)象保留的區(qū)別是什么呢?對(duì)于用 Java 語(yǔ)言編寫的程序來(lái)說(shuō),確實(shí)沒(méi)有區(qū)別。兩者都是指在您的程序中存在一些對(duì)象引用,但實(shí)際上您并不需要引用這些對(duì)象。一個(gè)典型的例子是向一個(gè)集合中加入一些對(duì)象以便以后使用它們,但是您卻忘了在使用完以后從集合中刪除這些對(duì)象。因?yàn)榧峡梢詿o(wú)限制地?cái)U(kuò)大,并且從來(lái)不會(huì)變小,所以當(dāng)您在集合中加入了太多的對(duì)象(或者是有很多的對(duì)象被集合中的元素所引用)時(shí),您就會(huì)因?yàn)槎训目臻g被填滿而導(dǎo)致內(nèi)存耗盡的錯(cuò)誤。垃圾收集器不能收集這些您認(rèn)為已經(jīng)用完的對(duì)象,因?yàn)閷?duì)于垃圾收集器來(lái)說(shuō),應(yīng)用程序仍然可以通過(guò)這個(gè)集合在任何時(shí)候訪問(wèn)這些對(duì)象,所以這些對(duì)象是不可能被當(dāng)作垃圾的。

  對(duì)于沒(méi)有垃圾收集的語(yǔ)言來(lái)說(shuō),例如 C++ ,內(nèi)存泄漏和無(wú)意識(shí)的對(duì)象保留是有區(qū)別的。C++ 程序跟 Java 程序一樣,可能產(chǎn)生無(wú)意識(shí)的對(duì)象保留。但是 C++ 程序中存在真正的內(nèi)存泄漏,即應(yīng)用程序無(wú)法訪問(wèn)一些對(duì)象以至于被這些對(duì)象使用的內(nèi)存無(wú)法釋放且返還給系統(tǒng)。令人欣慰的是,在 Java 程序中,這種內(nèi)存泄漏是不可能出現(xiàn)的。所以,我們更喜歡用“無(wú)意識(shí)的對(duì)象保留”來(lái)表示這個(gè)令 Java 程序員抓破頭皮的內(nèi)存問(wèn)題。這樣,我們就能區(qū)別于其他使用沒(méi)有垃圾收集語(yǔ)言的程序員。

  跟蹤被保留的對(duì)象

  那么當(dāng)發(fā)現(xiàn)了無(wú)意識(shí)的對(duì)象保留該怎么辦呢?首先,需要確定哪些對(duì)象是被無(wú)意保留的,并且需要找到究竟是哪些對(duì)象在引用它們。然后必須安排好 應(yīng)該在哪里釋放它們。最容易的方法是使用能夠?qū)Χ旬a(chǎn)生快照的檢測(cè)工具來(lái)標(biāo)識(shí)這些對(duì)象,比較堆的快照中對(duì)象的數(shù)目,跟蹤這些對(duì)象,找到引用這些對(duì)象的對(duì)象,然后強(qiáng)制進(jìn)行垃圾收集。有了這樣一個(gè)檢測(cè)器,接下來(lái)的工作相對(duì)而言就比較簡(jiǎn)單了:

  等待直到系統(tǒng)達(dá)到一個(gè)穩(wěn)定的狀態(tài),這個(gè)狀態(tài)下大多數(shù)新產(chǎn)生的對(duì)象都是暫時(shí)的,符合被收集的條件;這種狀態(tài)一般在程序所有的初始化工作都完成了之后。
  強(qiáng)制進(jìn)行一次垃圾收集,并且對(duì)此時(shí)的堆做一份對(duì)象快照。
  進(jìn)行任何可以產(chǎn)生無(wú)意地保留的對(duì)象的操作。
  再?gòu)?qiáng)制進(jìn)行一次垃圾收集,然后對(duì)系統(tǒng)堆中的對(duì)象做第二次對(duì)象快照。
  比較兩次快照,看看哪些對(duì)象的被引用數(shù)量比第一次快照時(shí)增加了。因?yàn)槟诳煺罩皬?qiáng)制進(jìn)行了垃圾收集,那么剩下的對(duì)象都應(yīng)該是被應(yīng)用程序所引用的對(duì)象,并且通過(guò)比較兩次快照我們可以準(zhǔn)確地找出那些被程序保留的、新產(chǎn)生的對(duì)象。
  根據(jù)您對(duì)應(yīng)用程序本身的理解,并且根據(jù)對(duì)兩次快照的比較,判斷出哪些對(duì)象是被無(wú)意保留的。
  跟蹤這些對(duì)象的引用鏈,找出究竟是哪些對(duì)象在引用這些無(wú)意地保留的對(duì)象,直到您找到了那個(gè)根對(duì)象,它就是產(chǎn)生問(wèn)題的根源。