深刻 Java 虛擬機之面試總結篇


在學習 JVM 相關知識,怎麼讓本身有動力看下去,且有思考性呢?筆者認爲,開頭用一些經常使用的面試題,來引入讀者的興趣比較好,這樣纔會有看下去的動力。因此,該篇文章會以面試+總結的方式,但願讀者能先思考寫出答案,再查看相關知識。web

1、JVM常見面試題面試

  • 介紹下 Java 內存區域
  • Java 對象的建立過程
  • 對象的訪問定位有幾種
  • String、StringBuilder、StringBuffer 有什麼不一樣?

這是一些常見的面試,不少人都看到網上的標準答案,但你知道爲何嗎?算法

1.1 介紹下 Java 內存區域安全

首先看第一個,Java的內存區域,能夠看一張編譯圖:app


4360b86faae77be6d8a8c74037c94e2e.jpeg


能夠看到Java 的內存區域就是框框裏的東西:ide


0a6f1deccffb6fd39940d96db8dd7223.jpeg


總結,建議讀者學習以後,能本身默寫這些方法並指導每一步的意思。post

1.2 Java 對象的建立過程學習

Java 對象的建立共分爲5步,以下圖:ui


edf35a77bc863e9944a80bb0017b2c13.jpeg


而後明白每一個步驟作了哪些便可,以下:.net


cc4e513b55dbc6dfebb840b1469f2a37.jpeg


1.3 對象的訪問定位有幾種

有兩種方式:句柄和直接指針。

建立對象是爲了使用對象,虛擬機須要經過棧中的 reference 來獲取堆上的對象。


292250ab6294ab90abc452e026d567d1.jpeg


優缺點: 使用句柄好處是,當對象發生改變,只須要移動句柄的實例數據指針便可,而直接指針就是速度快。

1.4 String、StringBuilder、StringBuffer

參考答案

String 是用 final 修飾的類,因爲它的不可變性,相似拼接、裁剪字符串等,都會產生新的對象。

StringBuffer 解決上面拼接對象而提供一個類,能夠經過 append等方法拼接,是線程安全的,因爲線程安全,效率也降低。

StringBuilder 跟StringBuffer 差很少,只是去掉了線程安全,因此優先使用 StringBuilder。

說說String 爲何會產生新的對象?好比 String a = "1" String b = a + "2",當執行這條指令時,會在常量池中產生一個對象指向a,而建立b時也會從新在常量池中生成b的對象;屢次建立容易觸發 GC,這也是爲何不建議使用 String 類去拼接的問題。

2、Java 回收機制常見面試題

  • 簡單的介紹一下強引用、軟引用、弱引用、虛引用(虛引用與軟引用和弱引用的區別、使用軟引用能帶來的好處)
  • 談談final、finally、finalize 有什麼不一樣
  • 方法區會回收資源嗎?
  • 垃圾回收有哪些算法,各自的特色?

2.1 強引用、軟引用、弱引用、虛引用

首先,在講解這幾個引用以前,先明白虛擬機爲何會由這些引用的說明;咱們都知道,對象須要回收,那怎麼去判斷哪些對象須要回收呢?這就須要一些判斷來肯定哪些對象是須要回收的,通常有如下幾種方法:


d4272e7bd9c15082274df38d5eb734f0.jpeg


不管是 引用計算算法仍是可達性分析算法,都是涉及到對象的引用問題,因此,在 JDK1.2 以後,又分爲如下幾類引用:


eaf90d4b601399e07d273724137c793b.jpeg


經過上面的介紹,知道了"引用"是什麼關係,這對理解各類引用仍是頗有必要的,那麼使用 軟引用的好處也在那裏了,建議一些內存消耗較大的使用軟引用,好比 webview。

2.2 談談final、finally、finalize 有什麼不一樣

final 和finally 比較好理解。首先 final 用來修飾的對象不可變;finally 則是保證重點代碼必定要被執行的一種機制,通常用於 try - catch-finally 語句中。

但finalize 是什麼東西呢?在解釋標準代碼以前,又得回到GC算法中了。

首先,finalize 是 Object 的一個方法,用來特定資源的回收。上面說到,當 GC Roots 不可達時,認爲對象已經再也不使用了,可是對象並不是是非"死"不可,當 GC Roots 不可達時,系統首先會先判斷 對象的 finalize 是否執行,不執行則直接回收。若是能夠執行,則放在隊列中,由finalize線程去執行它,若是有其餘對象關聯時,則判斷對象不可回收,不然對象回收,finalize 執行一次,以下圖:


917a2383d686837077d39296b8748433.jpeg


因爲它的不肯定性,在 JDK9時,已經標註爲deprecated,但不影響咱們對它的理解。

2.3 方法區會回收資源嗎?

雖然說 Java 堆 能夠回收70%~95%的空間,但方法區一樣能夠回收一些資源,方法區主要回收兩個部分廢棄常量無用的類


27e7205482cce8c33d4eff87b0b6d666.jpeg


因此,當發生 GC 時,很是常量和無用類是能夠被回收,固然這裏也是說"能夠",是否像對象同樣被回收,還須要對虛擬機的參數配置,這裏就不細說了。

2.4 垃圾回收有哪些算法,各自的特色?

對象的回收,基於上面講到的,GC Roots不可達,且判斷能夠回收。衍生的算法以下圖(建議能默認每種算法的理解):


279b95d55f6e03ad147e9aec4423df41.jpeg


其中,基礎是 標記-清除是基礎,接下來都是在它的基礎上改進,分代算法是主流 Java 虛擬機的主要算法。


3d4e137fdd7e3a9b9dd965510bd5b651.jpeg


3、類加載的問題

  • 類加載過程
  • 寫出下列代碼打印信息,若將System.out.println(Child.c_value)改成System.out.println(Child.value)會如何?
public class Parent{
 static {
 System.out.println("Parent");
 }
 public static int value = 123;
}
public class Child extends Parent{
 static {
 System.out.println("Child");
 }
 public static int c_value = 123;
}
//mian 中執行
public static void main(String[] args) {
 System.out.println(Child.c_value);
}
  • 說說你對類加載器的理解
  • 什麼是雙親委派模型

3.1 類加載的過程

類加載的過程以下圖所示(建議能默認每一個步驟的理解):


2b35178e5ef5754f66254fe4bf497e02.jpeg


也能夠成爲 加載-鏈接-初始化 這種叫法。

其中,加載、驗證、準備、初始化和卸載的順序是固定的,而解析則不必定,由於Java是動態語言,它能夠在運行時解析,即初始化以後。該階段解析以下:


ecc974ec3e4b402e78ae36e11f59d775.jpeg


3.2 Child.value和Child.c_value

public class Parent{
 static {
 System.out.println("Parent");
 }
 public static int value = 123;
}
public class Child extends Parent{
 static {
 System.out.println("Child");
 }
 public static int c_value = 123;
}
//mian 中執行
public static void main(String[] args) {
 System.out.println(Child.c_value);
}

打印信息如:

Parent
123


改成System.out.println(Child.value)時:

Parent
Child
123

具體看《深刻Java虛擬機之 -- 類加載機制》:https://blog.csdn.net/u011418943/article/details/89314894

擴展

class Parent{
 public static int value = 1;
 static {
 value = 2;
 }
 }
class Child extends Parent{
 public static int B = value ;
}
public static void main(String[] args) {
 System.out.println(Child.B);
}

輸出什麼?

3.3 說說你對類加載器的理解

從上面咱們知道,類在加載的時候,就是經過一個全限定名去加載這個類的二進制字節流,這個是系統自動完成的。這個動做若是從外部去作,以便於咱們去獲取所需的類,則咱們成爲類加載器。好比經過一個路徑獲取到一個 class 字節碼,而後經過反射,拿到相應的信息。

3.4 什麼是雙親委派模型

它的工程流程是:當一個類加載器收到類加載的請求,它首先不會本身去嘗試加載這個類,而是委派給她的父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載器都會傳遞到父加載器中。只有父加載器沒法完成時,子加載器纔會嘗試本身去加載,它的模型以下:


d90fa6f4abe1d5c284c27df59a22d874.jpeg


類加載雙親委派模型

原文連接:https://juejin.im/post/5ccfa05af265da039f0f243b

相關文章
相關標籤/搜索