JVM中的對象生命周期

在JVM運(yùn)行空間中,對象的整個(gè)生命周期大致可以分為7個(gè)階段:創(chuàng)建階段(Creation)、應(yīng)用階段(Using)、不可視階段(Invisible)、不可到達(dá)階段(Unreachable)、可收集階段(Collected)、終結(jié)階段(Finalized)與釋放階段(Free)。

創(chuàng)建階段

在對象創(chuàng)建階段,系統(tǒng)要通過下面的步驟,完成對象的創(chuàng)建過程:

(1)為對象分配存儲(chǔ)空間。

(2)開始構(gòu)造對象。

(3)遞歸調(diào)用其超類的構(gòu)造方法。

(4)進(jìn)行對象實(shí)例初始化與變量初始化。

(5)執(zhí)行構(gòu)造方法體。

上面的5個(gè)步驟中的第3步就是指遞歸地調(diào)用該類所擴(kuò)展的所有父類的構(gòu)造方法,一個(gè)Java類(除Object類外)至少有一個(gè)父類(Object),這個(gè)規(guī)則既是強(qiáng)制的,也是隱式的。你可能已經(jīng)注意到在創(chuàng)建一個(gè)Java類的時(shí)候,并沒有顯式地聲明擴(kuò)展(extends)一個(gè)Object父類。實(shí)際上,在Java程序設(shè)計(jì)中,任何一個(gè)Java類都直接或間接的是Object類的子類。例如下面的代碼:

這個(gè)聲明等同于下面的聲明:

上面講解了對象處于創(chuàng)建階段時(shí),系統(tǒng)所做的一些處理工作,其中有些過程與應(yīng)用的性能密切相關(guān),因此在創(chuàng)建對象時(shí),我們應(yīng)該遵循一些基本的規(guī)則,以提高應(yīng)用的性能。

下面是JVM對象生命周期在創(chuàng)建對象時(shí)的幾個(gè)關(guān)鍵應(yīng)用規(guī)則:

(1)避免在循環(huán)體中創(chuàng)建對象,即使該對象占用內(nèi)存空間不大。

(2)盡量及時(shí)使對象符合垃圾回收標(biāo)準(zhǔn)。

(3)不要采用過深的繼承層次。

(4)訪問本地變量優(yōu)于訪問類中的變量。

關(guān)于規(guī)則(1)避免在循環(huán)體中創(chuàng)建對象,即使該對象占用內(nèi)存空間不大,需要提示一下,這種情況在我們的實(shí)際應(yīng)用中經(jīng)常遇到,而且我們很容易犯類似的錯(cuò)誤,例如下面的代碼:

上面代碼的書寫方式相信對你來說不會(huì)陌生,也許在以前的應(yīng)用開發(fā)中你也這樣做過,尤其是在枚舉一個(gè)Vector對象中的對象元素的操作中經(jīng)常會(huì)這樣書寫,但這卻違反了上述規(guī)則(1),因?yàn)檫@樣會(huì)浪費(fèi)較大的內(nèi)存空間,正確的方法如下所示:

采用上面的第二種編寫方式,僅在內(nèi)存中保存一份對該對象的引用,而不像上面的第一種編寫方式中代碼會(huì)在內(nèi)存中產(chǎn)生大量的對象應(yīng)用,浪費(fèi)大量的內(nèi)存空間,而且增大了系統(tǒng)做垃圾回收的負(fù)荷。因此在循環(huán)體中聲明創(chuàng)建對象的編寫方式應(yīng)該盡量避免。

另外,不要對一個(gè)對象進(jìn)行多次初始化,這同樣會(huì)帶來較大的內(nèi)存開銷,降低系統(tǒng)性能,如:

正確的方式為:

不要小看這個(gè)差別,它卻使應(yīng)用軟件的性能相差甚遠(yuǎn),如圖所示:

初始化對象多次所帶來的性能差別

看來在程序設(shè)計(jì)中也應(yīng)該遵從“勿以惡小而為之”的古訓(xùn),否則我們開發(fā)出來的應(yīng)用也是低效的應(yīng)用,有時(shí)應(yīng)用軟件中的一個(gè)極小的失誤,就會(huì)大幅度地降低整個(gè)系統(tǒng)的性能。因此,我們在日常的應(yīng)用開發(fā)中,應(yīng)該認(rèn)真對待每一行代碼,采用最優(yōu)化的編寫方式,不要忽視細(xì)節(jié),不要忽視潛在的問題。

應(yīng)用階段

當(dāng)對象的創(chuàng)建階段結(jié)束之后,該對象通常就會(huì)進(jìn)入對象的應(yīng)用階段。這個(gè)階段是對象得以表現(xiàn)自身能力的階段。也就是說對象的應(yīng)用階段是對象整個(gè)生命周期中證明自身“存在價(jià)值”的時(shí)期。在對象的應(yīng)用階段,對象具備下列特征:

◆系統(tǒng)至少維護(hù)著對象的一個(gè)強(qiáng)引用(StrongReference);

◆所有對該對象的引用全部是強(qiáng)引用(除非我們顯式地使用了:軟引用(SoftReference)、弱引用(WeakReference)或虛引用(PhantomReference))。

上面提到了幾種不同的引用類型。可能一些讀者對這幾種引用的概念還不是很清楚,下面分別對之加以介紹。在講解這幾種不同類型的引用之前,我們必須先了解一下Java中對象引用的結(jié)構(gòu)層次。

Java對象生命周期引用的結(jié)構(gòu)層次示意,如圖所示。


對象引用的結(jié)構(gòu)層次示意

上面所提到的幾種JVM對象生命周期引用的層次關(guān)系,其中強(qiáng)引用處于頂端,而虛引用則處于底端。下面分別予以介紹。

1.強(qiáng)引用

強(qiáng)引用(StrongReference)是指JVM內(nèi)存管理器從根引用集合(RootSet)出發(fā)遍尋堆中所有到達(dá)對象的路徑。當(dāng)?shù)竭_(dá)某對象的任意路徑都不含有引用對象時(shí),對這個(gè)對象的引用就被稱為強(qiáng)引用。

2.軟引用

軟引用(SoftReference)的主要特點(diǎn)是具有較強(qiáng)的引用功能。只有當(dāng)內(nèi)存不夠的時(shí)候,才回收這類內(nèi)存,因此在內(nèi)存足夠的時(shí)候,它們通常不被回收。另外,這些引用對象還能保證在Java拋出OutOfMemory異常之前,被設(shè)置為null。它可以用于實(shí)現(xiàn)一些常用資源的緩存,實(shí)現(xiàn)Cache的功能,保證最大限度的使用內(nèi)存而不引起OutOfMemory。再者,軟可到達(dá)對象的所有軟引用都要保證在虛擬機(jī)拋出OutOfMemoryError之前已經(jīng)被清除。否則,清除軟引用的時(shí)間或者清除不同對象的一組此類引用的順序?qū)⒉皇苋魏渭s束。然而,虛擬機(jī)實(shí)現(xiàn)不鼓勵(lì)清除最近訪問或使用過的軟引用。下面是軟引用的實(shí)現(xiàn)代碼:

軟引用技術(shù)的引進(jìn),使Java應(yīng)用可以更好地管理內(nèi)存,穩(wěn)定系統(tǒng),防止系統(tǒng)內(nèi)存溢出,避免系統(tǒng)崩潰(crash)。因此在處理一些占用內(nèi)存較大而且聲明周期較長,但使用并不頻繁的對象時(shí)應(yīng)盡量應(yīng)用該技術(shù)。正像上面的代碼一樣,我們可以在對象被回收之后重新創(chuàng)建(這里是指那些沒有保留運(yùn)行過程中狀態(tài)的對象),提高應(yīng)用對內(nèi)存的使用效率,提高系統(tǒng)穩(wěn)定性。

但事物總是帶有兩面性的,有利亦有弊。在某些時(shí)候?qū)浺玫氖褂脮?huì)降低應(yīng)用的運(yùn)行效率與性能,例如:應(yīng)用軟引用的對象的初始化過程較為耗時(shí),或者對象的狀態(tài)在程序的運(yùn)行過程中發(fā)生了變化,都會(huì)給重新創(chuàng)建對象與初始化對象帶來不同程度的麻煩,有些時(shí)候我們要權(quán)衡利弊擇時(shí)應(yīng)用。

3.弱引用

弱引用(WeakReference)對象與Soft引用對象的最大不同就在于:GC在進(jìn)行回收時(shí),需要通過算法檢查是否回收Soft引用對象,而對于Weak引用對象,GC總是進(jìn)行回收。因此Weak引用對象會(huì)更容易、更快被GC回收。雖然,GC在運(yùn)行時(shí)一定回收Weak引用對象,但是復(fù)雜關(guān)系的Weak對象群常常需要好幾次GC的運(yùn)行才能完成。Weak引用對象常常用于Map數(shù)據(jù)結(jié)構(gòu)中,引用占用內(nèi)存空間較大的對象,一旦該對象的強(qiáng)引用為null時(shí),對這個(gè)對象引用就不存在了,GC能夠快速地回收該對象空間。與軟引用類似我們也可以給出相應(yīng)的應(yīng)用代碼:

弱引用技術(shù)主要適用于實(shí)現(xiàn)無法防止其鍵(或值)被回收的規(guī)范化映射。另外,弱引用分為“短弱引用(ShortWeekReference)”和“長弱引用(LongWeekReference)”,其區(qū)別是長弱引用在對象的Finalize方法被GC調(diào)用后依然追蹤對象。基于安全考慮,不推薦使用長弱引用。因此建議使用下面的方式創(chuàng)建對象的弱引用。

4.虛引用

虛引用(PhantomReference)的用途較少,主要用于輔助finalize函數(shù)的使用。Phantom對象指一些執(zhí)行完了finalize函數(shù),并且為不可達(dá)對象,但是還沒有被GC回收的對象。這種對象可以輔助finalize進(jìn)行一些后期的回收工作,我們通過覆蓋Reference的clear()方法,增強(qiáng)資源回收機(jī)制的靈活性。虛引用主要適用于以某種比java終結(jié)機(jī)制更靈活的方式調(diào)度pre-mortem清除操作。

&注意在實(shí)際程序設(shè)計(jì)中一般很少使用弱引用與虛引用,使用軟引用的情況較多,這是因?yàn)檐浺每梢约铀貸VM對垃圾內(nèi)存的回收速度,可以維護(hù)系統(tǒng)的運(yùn)行安全,防止內(nèi)存溢出(OutOfMemory)等問題的產(chǎn)生。

不可視階段

在一個(gè)對象經(jīng)歷了應(yīng)用階段之后,那么該對象便處于JVM對象生命周期的不可視階段,說明我們在其他區(qū)域的代碼中已經(jīng)不可以再引用它,其強(qiáng)引用已經(jīng)消失,例如,本地變量超出了其可視范圍,如下所示。

如果一個(gè)對象已使用完,而且在其可視區(qū)域不再使用,此時(shí)應(yīng)該主動(dòng)將其設(shè)置為空(null)。可以在上面的代碼行obj.doSomething();下添加代碼行obj=null;,這樣一行代碼強(qiáng)制將obj對象置為空值。這樣做的意義是,可以幫助JVM及時(shí)地發(fā)現(xiàn)這個(gè)垃圾對象,并且可以及時(shí)地回收該對象所占用的系統(tǒng)資源。

不可到達(dá)階段

處于JVM對象生命周期不可到達(dá)階段的對象,在虛擬機(jī)所管理的對象引用根集合中再也找不到直接或間接的強(qiáng)引用,這些對象通常是指所有線程棧中的臨時(shí)變量,所有已裝載的類的靜態(tài)變量或者對本地代碼接口(JNI)的引用。這些對象都是要被垃圾回收器回收的預(yù)備對象,但此時(shí)該對象并不能被垃圾回收器直接回收。其實(shí)所有垃圾回收算法所面臨的問題是相同的——找出由分配器分配的,但是用戶程序不可到達(dá)的內(nèi)存塊。

可收集階段、終結(jié)階段與釋放階段

JVM對象生命周期的最后一個(gè)階段是可收集階段、終結(jié)階段與釋放階段。當(dāng)對象處于這個(gè)階段的時(shí)候,可能處于下面三種情況:

(1)垃圾回收器發(fā)現(xiàn)該對象已經(jīng)不可到達(dá)。

(2)finalize方法已經(jīng)被執(zhí)行。

(3)對象空間已被重用。

當(dāng)對象處于上面的三種情況時(shí),該對象就處于可收集階段、終結(jié)階段與釋放階段了。虛擬機(jī)就可以直接將該對象回收了。

參考資料


Java優(yōu)化編程-林勝利

解讀JVM對象生命周期的幾個(gè)階段

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,993評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內(nèi)容

  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虛擬機(jī)(JVM)垃圾回收器提供...
    簡欲明心閱讀 89,738評論 17 311
  • 1.一些概念 1.1.數(shù)據(jù)類型 Java虛擬機(jī)中,數(shù)據(jù)類型可以分為兩類:基本類型和引用類型。基本類型的變量保存原始...
    落落落落大大方方閱讀 4,555評論 4 86
  • 作者:一字馬胡 轉(zhuǎn)載標(biāo)志 【2017-11-12】 更新日志 日期更新內(nèi)容備注 2017-11-12新建文章初版 ...
    beneke閱讀 2,222評論 0 7
  • 原文閱讀 前言 這段時(shí)間懈怠了,罪過! 最近看到有同事也開始用上了微信公眾號(hào)寫博客了,挺好的~給他們點(diǎn)贊,這博客我...
    碼農(nóng)戲碼閱讀 6,005評論 2 31
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方,同時(shí)不同JDK版本的...
    高廣超閱讀 15,651評論 3 83