1. 程式人生 > 程式設計 >一個Java物件的死亡證明

一個Java物件的死亡證明

本文首發至java技術部落格[碼上]:jdkcb.com/

前言:

生的對立面不是死亡,而是遺忘。

生的對立面不是死亡,而是遺忘。

生的對立面不是死亡,而是遺忘。

重要的事情說三遍!大家請一定要好好記住這句話,這句話是我們整篇文章的精髓所在。

我信你個鬼,你個世界上最帥最機智勇敢玉樹臨風風流倜儻的帥哥壞得很。

不信?走著瞧,扯不上去算我輸。

概念準備:

VM 洋文全稱為Virtual Machine ,中文一般被譯為虛擬機器器,虛擬機器器一般用來指通過軟體模擬的具有完整硬體系統功能的、執行在一個完全隔離環境中的完整計算機系統。

而 JVM則是代表java語言的虛擬機器器,洋文全稱:Java Virtual Machine。

世界上第一款商用java虛擬機器器是Sun公司1996年在jdk1.0中釋出的Sun Classic VM,很早之前Sun Classic VM就已經被廢棄掉了,由HotSot VM虛擬機器器來代替。

扯蛋環節:

既然你是要寫jvm記憶體回收系列的學習筆記,那關java物件死亡有什麼關係?

誒,這你就不對了,怎麼能沒有關係呢,jvm回收的是什麼?是記憶體,記憶體被誰佔著呢?java物件(主要),那物件不死怎麼回收他呢?

物件不死怎麼就不能回收了?

槓是吧,扛是吧,扛是吧,我們把jvm虛擬機器器在垃圾回收過程中的角色理解為我們神話傳說中的閻王爺,按照古老的傳說,人只有死了之後靈魂才會被回收繼續投胎,人活著就算再廢物也不能直接讓他投胎了,必須要等他死了才行,而且,在人沒死之前直接回收這個人的靈魂可能會導致一些很嚴重的問題,比如你回收個美國的殺人犯的靈魂,那肯定對這個世界不會產生什麼特別大的影響,但是萬一你要不小心抽到特朗普了,那可能就會引起大亂子。

jvm也是這麼考慮的,萬一你這個物件別的地方正用著呢,jvm直接給他回收了,就有可能直接導致整個應用的崩潰。

微劇場:

大家好,我是一個java物件,我叫阿呆。萬萬沒想到的是,我現在竟然要證明自己死了。

事情是這樣的,我是一個廢材物件,當主人把我創造出來時,我滿以為自己會光芒萬丈大有作為成為java物件之王,可是這貨把我new出來之後他孃的竟然把我給忘了,他和其他的物件嬉戲打鬧,我卻只能在角落裡傷心假笑,我感覺整個執行環境的天空都變成了灰色,我不想活了,我太難了,於是我跑到java虛擬機器器記憶體回收局懇請可以將我回收掉,像我這種廢材沒人在乎的物件真的就是活著浪費記憶體了。

可是java虛擬機器器記憶體回收局殘忍地拒絕了我,還讓我哪涼快哪呆著去,當然我並不打算為此退縮,在我的威逼利誘(苦苦哀求)下,java虛擬機器器記憶體回收局終於告訴了我事情的真相。

很遺憾,阿呆先生,這個世界上還有人仍然記得你,所以我們不能把你回收掉。

還有人記得..記得我?(雙眼閃爍著淚花)

聽完阿呆屁顛屁顛邁著扭秧歌的魔鬼的步伐回去了。(尼瑪,剛才還想自殺呢,節操呢?變這麼快)

當然,java虛擬機器器記憶體回收局肯定不是隨隨便便就把阿呆認定死亡的,經歷過幾十年的發展,該機構研發了一款java物件驗死儀,原理主要是基於引用計數可達性分析演演算法這兩個演演算法,那麼下面我們就依次來介紹這兩種演演算法。

引用計數演演算法:

官方概念:引用計數演演算法是通過判斷物件的引用數量來決定物件是否可以被回收。

Hello h = new Hello(); 其中h就稱之為Hello物件的引用,即我們可以通過h來訪問到我們的Hello物件。

在這裡我們可以理解為,假如B物件中包含A物件的引用,就意味著B物件和A物件有所聯絡,即B物件記得A物件。當B物件因為一些原因比如意外死亡,老年痴呆,韓國偶像劇等,不記得A物件的存在了,則我們稱之為B遺忘了A,我說嘛,我肯定可以扯到這裡的。

每當有一個新的物件和A物件產生聯絡時,即引用A物件,那麼A物件的引用計數器就+1,如果誰不小心把A物件給忘了, 則意味著引用失效了,那麼A物件的引用計數器就-1,當A物件的引用計數器變成0的時候,意味著虛擬機器器中已經沒有任何物件還保留著A物件的引用了,即除了A物件自己,沒有物件記得A物件的存在了,這個時候A物件就會被判定為死亡,被記憶體回收掉。

這個時候可能有善良的小夥伴問了,A物件那麼可愛,為什麼要回收掉它呢,留著它不好嗎。

現在我們迴歸引用的概念,A物件引用計數器為0意味著再也沒有任何一個物件可以訪問到A物件了,側面就說明瞭A物件再也不能被使用,變成了一個徹徹底底的廢物件。

所以:生的對立面不是死亡,而是遺忘。

這麼一想,引用計數演演算法的確還不錯,這樣死亡的物件都會被正確識別出來,從而回收掉。

年輕人,你還是想的太簡單了。你忘了這個世界上有奇葩的存在了嗎?且看下面這段對話:

A:我詛咒你今年禿頭!

B:詛咒反貪

A:我也反彈

B:我再反彈

此處省略N個字。

沒完沒了,放在物件也是這樣,加入A物件包含了B物件的引用,B物件又包含了A物件的引用,因為相互引用,他們的引用計數器都無法為0,也就不會被回收掉了。

所以引用計數在目前主流的java虛擬機器器中被廢棄掉了。

可達性分析演演算法:

官方概念:可達性分析演演算法是通過判斷物件的引用鏈是否可達來決定物件是否可以被回收。

從GC Roots作為起點,向下搜尋它們引用的物件,可以生成一棵引用樹,樹的節點視為可達物件,反之視為不可達。圖長這樣:

可達性分析演演算法

這個演演算法就好玩多了,我們可以把這個演演算法看作是一個巨大的傳銷集團,而GC Roots就是這個集團的傳銷頭子 ,那它是怎麼判斷物件是否死亡的呢?

當需要記憶體回收的時候,虛擬機器器會沿著傳銷頭子往下依次搜尋它的下線,如果這個物件能被搜尋到,那麼就意味著這個物件是在傳銷組織裡面的,比如Object2 3 4 都在傳銷集團裡面,而Object 5 6 7 呢。

Object 5 6 7就是那些沒有組織的野傳銷,傳銷集團怎麼會容忍這些野傳銷存在呢,於是他們都被判斷為死亡。

所以:生的對立面不是死亡,而是遺忘。

而 Object 5 6 7 這三個物件都被遺忘掉了,因為他們沒有組織,因為失去了和組織的聯絡,所以組織也沒有辦法差遣他們,發揮他們的價值,於是它們變成廢物件被回收掉了。

也就是說呢,在可達性分析演演算法中,如果一個物件無法被虛擬機器器沿著GC Roots依次向下搜尋到,那麼這個物件就是不可達的,因為沒有任何一條路通往這個物件,這個物件就像是海上的孤島一樣,最後只能被java虛擬機器器回收。

的確優勢明顯,隨便你們反彈,沒有組織你們都要完蛋。

而GC Roots也不是誰都能當的,在長期的實踐中,發現有四類物件擁有成為傳銷頭子,呸,Gc Roots的潛質,他們分別是:

  • 虛擬機器器棧(棧幀中的本地變量表)中的引用物件。
  • 方法區中的類靜態屬性引用的物件。
  • 方法區中的常量引用的物件。
  • 本地方法棧中JNI(Native方法)的引用物件

正因為可達性分析在實際使用中的優異表現,所以很多現代虛擬機器器都採用了這一實現。

珍愛生命,遠離傳銷,珍愛生命,遠離傳銷,珍愛生命,遠離傳銷!

下面到技術總結環節:

總結:

本篇文章我們可謂是用盡了廢話來幫助剛開始學習java或者jvm的朋友們徹底理解jvm判斷java物件死亡的兩種演演算法,再學習jvm的過程中,發現很多jvm書籍,部落格都寫的比較學術,讓新手們望而卻步,導致很多人看了一遍以為自己懂了,但是過了幾天之後再想卻沒有辦法想起來,根本原因還是在於並沒有徹底的理解這些知識,為了幫助一些朋友更好的理解jvm虛擬機器器,所以才有了jvm學習筆記系列,同樣的,本系列筆記會開源至github,並保持更新。

我是韓數,我們下篇文章再見。