本文已受權玉剛說公衆號web
系列文章:面試
在學習 JVM 相關知識,怎麼讓本身有動力看下去,且有思考性呢?筆者認爲,開頭用一些經常使用的面試題,來引入讀者的興趣比較好,這樣纔會有看下去的東西,因此,該篇文章會以面試+總結的方式,但願讀者能先思考寫出答案,再查看相關知識。spa
這是一些常見的面試,不少人都看到網上的標準答案,但你知道爲何嗎?
首先看第一個,Java的內存區域,能夠看一張編譯圖:
Java 對象的建立共分爲5步,以下圖:
有兩種方式:句柄和直接指針; 建立對象是爲了使用對象,虛擬機須要經過棧中的 reference 來獲取堆上的對象。
參考答案是: String 是用 final 修飾的類,因爲它的不可變性,相似拼接、裁剪字符串等,都會產生新的對象。 StringBuffer 解決上面拼接對象而提供一個類,能夠經過 append等方法拼接,是線程安全的,因爲線程安全,效率也降低 StringBuilder 跟StringBuffer 差很少,只是去掉了線程安全,因此優先使用 StringBuilder
說說String 爲何會產生新的對象?好比 String a = "1" String b = a + "2",當執行這條指令時,會在常量池中產生一個對象指向a,而建立b時也會從新在常量池中生成b的對象;屢次建立容易觸發 GC,這也是爲何不建議使用 String 類去拼接的問題。
深刻Java虛擬機之 --- JVM的愛恨情仇 JAVA 垃圾回收機制(二) --- GC回收具體實現
首先,在講解這幾個引用以前,先明白虛擬機爲何會由這些引用的說明;咱們都知道,對象須要回收,那怎麼去判斷哪些對象須要回收呢?這就須要一些判斷來肯定哪些對象是須要回收的,通常有如下幾種方法:
final 和finally 比較好理解。首先 final 用來修飾的對象不可變;finally 則是保證重點代碼必定要被執行的一種機制,通常用於 try - catch-finally 語句中。 但finalize 是什麼東西呢?在解釋標準代碼以前,又得回到GC算法中了。 首先,finalize 是 Object 的一個方法,用來特定資源的回收。 上面說到,當 GC Roots 不可達時,認爲對象已經再也不使用了,可是對象並不是是非"死"不可,當 GC Roots 不可達時,系統首先會先判斷 對象的 finalize 是否執行,不執行則直接回收;若是能夠執行,則放在隊列中,由finalize線程去執行它,若是有其餘對象關聯時,則判斷對象不可回收,不然對象回收,finalize 執行一次,以下圖:
雖然說 Java 堆 能夠回收70%~95%的空間,但方法區一樣能夠回收一些資源,方法區主要回收兩個部分廢棄常量和無用的類。
對象的回收,基於上面講到的,GC Roots不可達,且判斷能夠回收。衍生的算法以下圖(建議能默認每種算法的理解):
深刻Java虛擬機之 -- 類文件結構(字節碼) 深刻Java虛擬機之 -- 類加載機制
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);
}
複製代碼
類加載的過程以下圖所示(建議能默認每一個步驟的理解):
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
Child
123
複製代碼
改成System.out.println(Child.value)時:
Parent
123
複製代碼
具體看:深刻Java虛擬機之 -- 類加載機制 擴展:
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);
}
複製代碼
輸出什麼?
從上面咱們知道,類在加載的時候,就是經過一個全限定名去加載這個類的二進制字節流,這個是系統自動完成的。這個動做若是從外部去作,以便於咱們去獲取所需的類,則咱們成爲類加載器。好比經過一個路徑獲取到一個 class 字節碼,而後經過反射,拿到相應的信息。
它的工程流程是: 當一個類加載器收到類加載的請求,它首先不會本身去嘗試加載這個類,而是委派給她的父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載器都會傳遞到父加載器中;只有父加載器沒法完成時,子加載器纔會嘗試本身去加載,它的模型以下: