跟面試官聊到JVM,他99%會讓你談談這個問題!

但凡問到 JVM(Java 虛擬機)一般有 99% 的機率必定會問: 在 JVM 中如何判斷一個對象的生死狀態?面試

本文就來聊聊這個問題,判斷對象的生死狀態的算法有如下幾個:算法

一、引用計數器算法

引用計算器判斷對象是否存活的算法是這樣的:給每個對象設置一個引用計數器,每當有一個地方引用這個對象的時候,計數器就加1,與之相反,每當引用失效的時候就減1。編程

優勢 :實現簡單、性能高。跨域

缺點 :增減處理頻繁消耗cpu計算、計數器佔用不少位浪費空間、最重要的缺點是沒法解決循環引用的問題。緩存

由於引用計數器算法很難解決循環引用的問題,因此主流的Java虛擬機都沒有使用引用計數器算法來管理內存。性能優化

運行的結果:架構

開始:117 M併發

運行中:96 M框架

結束:119 Mjvm

從結果能夠看出,虛擬機並無由於相互引用就不回收它們,也側面說明了虛擬機並非使用引用計數器實現的。

二、可達性分析算法

在主流的語言的主流實現中,好比Java、C#、甚至是古老的Lisp都是使用的可達性分析算法來判斷對象是否存活的。

這個算法的核心思路就是經過一些列的「GC Roots」對象做爲起始點,從這些對象開始往下搜索,搜索所通過的路徑稱之爲「 引用鏈 」。

當一個對象到GC Roots沒有任何引用鏈相連的時候,證實此對象是能夠被回收的。以下圖所示:

在Java中,可做爲GC Roots對象的列表:

Java虛擬機棧中的引用對象。

本地方法棧中JNI(既通常說的Native方法)引用的對象。

方法區中類靜態常量的引用對象。

方法區中常量的引用對象。

對象生死與引用的關係

從上面的兩種算法來看,不論是引用計數法仍是可達性分析算法都與對象的「引用」有關,這說明:對象的引用決定了對象的生死。

那對象的引用都有那些呢?

在JDK1.2以前,引用的定義很傳統:若是reference類型的數據中存儲的數值表明的是另外一塊內存的起始地址,就稱這塊內存表明着一塊引用。

這樣的定義很純粹,可是也很狹隘,這種狀況下一個對象要麼被引用,要麼沒引用,對於介於二者之間的對象顯得無能爲力。

JDK1.2以後對引用進行了擴充,將引用分爲:

強引用 (Strong Reference)

軟引用 (Soft Reference)

弱引用 (Weak Reference)

虛引用 (Phantom Reference)

這也就是文章開頭第一個問題的答案,對象不是非生即死的,當空間還足夠時,還能夠保留這些對象

若是空間不足時,再拋棄這些對象。不少緩存功能的實現也符合這樣的場景。

強引用、軟引用、弱引用、虛引用,這4種引用的強度是依次遞減的。

強引用 :在代碼中廣泛存在的,相似「Object obj = new Object()」這類引用,只要強引用還在,垃圾收集器永遠不會回收掉被引用的對象。

軟引用 :是一種相對強引用弱化一些的引用,可讓對象豁免一些垃圾收集,只有當jvm認爲內存不足時,纔會去試圖回收軟引用指向的對象。 jvm會確保在拋出OutOfMemoryError以前,清理軟引用指向的對象。

弱引用 :非必需對象,但它的強度比軟引用更弱,被弱引用關聯的對象只能生存到下一次垃圾收集發生以前。

虛引用 :也稱爲幽靈引用或幻影引用,是最弱的一種引用關係,沒法經過虛引用來獲取一個對象實例,爲對象設置虛引用的目的只有一個,就是當着個對象被收集器回收時收到一條系統通知。

死亡標記與拯救

在可達性算法中不可達的對象,並非「非死不可」的,要真正宣告一個對象死亡,至少要經歷兩次標記的過程。

若是對象在進行可達性分析以後,沒有與GC Roots相鏈接的引用鏈,它會被第一次標記,並進行篩選,篩選的條件是此對象是否有必要執行finalize()方法。

執行finalize()方法的兩個條件:

一、重寫了finalize()方法。

二、finalize()方法以前沒被調用過,由於對象的finalize()方法只能被執行一次。

若是知足以上兩個條件,這個對象將會放置在F-Queue的隊列之中,並在稍後由一個虛擬機自建的、低優先級Finalizer線程來執行它。

對象的「自我拯救」

finalize()方法是對象脫離死亡命運最後的機會,若是對象在finalize()方法中從新與引用鏈上的任何一個對象創建關聯便可,好比把本身(this關鍵字)賦值給某個類變量或對象的成員變量。

來看具體的實現代碼:

執行的結果:

執行finalize方法

我還活着

我已經死了

從結果能夠看出,任何對象的finalize()方法都只會被系統調用一次。

不建議使用finalize()方法來拯救對象 ,緣由以下:

一、對象的finalize()只能執行一次。

二、它的運行代價高昂。

三、不肯定性大。

四、沒法保證各個對象的調用順序。

下面分享架構師必會知識內容

如下僅列舉了部分,文末有架構師資料的獲取方法。

阿里P7架構師談:分佈式架構核心組件之消息隊列

阿里P7架構師談:跨域單點登陸分析及項目實戰

阿里P7架構師談:站在設計者的角度思考Synchronized的設計原理

阿里P7架構師談:分佈式架構通訊基礎NIO原理解讀

阿里P7架構師談:技術大師帶你玩轉Tomcat核心思想

阿里P7架構師談:基於Netty手寫Dubbo框架

阿里P7架構師談:深度分析併發編程的應用及原理

阿里P7架構師談:HashMap1.8源碼分析

阿里P7架構師談:MySQL索引機制的探祕

阿里P7架構師談:Redis互聯網應用場景總結

阿里P7架構師談:暢聊微服務架構與敏捷開發

阿里P7架構師談:想看源碼總暈車,方法技巧是關鍵

阿里P7架構師談:分佈式RPC手寫實現.

阿里P7架構師談:阿里面試p6必問之併發編程原理分析

阿里P7架構師談:面試必問之JVM

阿里P7架構師談:P7面試之Spring精華總結

領取方法

加架構羣:705127209領取資料,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的資料

相關文章
相關標籤/搜索