參考鏈接:https://juejin.im/post/5b45ef49f265da0f5140489c
JVM是否會一次性加載所有類?

三種默認(rèn)的類加載器及其工作職責(zé)?

雙親委派模型是什么?

JIT即時編譯器是什么?
【JIT編譯器詳解】https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/
Java字節(jié)碼重新編譯優(yōu)化,生成機器碼,讓CPU直接執(zhí)行,一般對熱點代碼做編譯,非熱點代碼直接解析就好了。
怎么區(qū)別是否是熱點代碼呢?通過“采樣”和“計數(shù)器”,HotSpot使用的是計數(shù)器的方法:“方法調(diào)用計數(shù)器”和“回編計數(shù)器”,在確定虛擬機運行參數(shù)的前提下,這兩個計數(shù)器都有一個確定的閾值,當(dāng)計數(shù)器超過閾值溢出了,就會觸發(fā)JIT編譯。

1) 為什么引入JIT即時編譯器?
java通過 javac 將程序源代碼編譯,轉(zhuǎn)換成 java 字節(jié)碼,JVM 通過解釋字節(jié)碼將其翻譯成對應(yīng)的機器指令,逐條讀入,逐條解釋翻譯。很顯然,經(jīng)過解釋執(zhí)行,其執(zhí)行速度必然會比可執(zhí)行的二進制字節(jié)碼程序慢很多。為了提高執(zhí)行速度,引入了 JIT 技術(shù)。
2) 寄存器優(yōu)化
寄存器的使用是編譯器的一個非常普遍的優(yōu)化。對一個變量多次進行操作,如果頻繁的從主存中獲取值,會帶來很大的開銷。編譯器加載一個寄存器給 sum 并賦予其初始值,利用寄存器里的值來執(zhí)行操作,并將最終的結(jié)果從寄存器返回給主存。
3) JIT編譯的兩種模式
客戶端模式和服務(wù)器模式:可以再運行時選擇其中一種編譯模式,-server 模式啟動時,速度較慢,但是一旦運行起來后,性能將會有很大的提升。當(dāng)虛擬機運行在-client 模式的時候,使用的是一個代號為 C1 的輕量級編譯器,而-server 模式啟動的虛擬機采用相對重量級代號為 C2 的編譯器。C2 比 C1 編譯器編譯的相對徹底,服務(wù)起來之后,性能更高。
4)優(yōu)化代碼緩存
JVM 編譯代碼時,它會將匯編指令集保存在代碼緩存。代碼緩存具有固定的大小,并且一旦它被填滿,JVM 則不能再編譯更多的代碼。沒有一個好的機制可以確定一個特定的應(yīng)用到底需要多大的代碼緩存,通常的做法是將代碼緩存變成默認(rèn)大小的兩倍或四倍。代碼緩存的初始大小是基于芯片架構(gòu)(例如 Intel 系列機器,client 編譯器模式下代碼緩存大小起始于 160KB,server 編譯器模式下代碼緩存大小則起始于 2496KB)以及使用的編譯器的。重定義代碼緩存的大小并不會真正影響性能,所以設(shè)置 ReservedCodeCacheSize 的大小一般是必要的。
5)編譯閾值
JVM 編譯代碼編譯是基于兩個計數(shù)器:一個是方法被調(diào)用的次數(shù),另一個是方法中循環(huán)被回彈執(zhí)行的次數(shù)?;貜椏梢杂行У谋徽J(rèn)為是循環(huán)被執(zhí)行完成的次數(shù),不僅因為它是循環(huán)的結(jié)尾,也可能是因為它執(zhí)行到了一個分支語句。檢查這兩個計數(shù)器的總和以決定這個方法是否有資格被編譯。如果方法里有一個很長的循環(huán)或者是一個永遠(yuǎn)都不會退出并提供了所有邏輯的程序會怎么樣呢?這種情況下,JVM 需要編譯循環(huán)而并不等待方法被調(diào)用。所以每執(zhí)行完一次循環(huán),分支計數(shù)器都會自增和自檢。如果分支計數(shù)器計數(shù)超出其自身閾值,那么這個循環(huán)(并不是整個方法)將具有被編譯資格。這種編譯叫做棧上替換(OSR),因為即使循環(huán)被編譯了,這也是不夠的:JVM 必須有能力當(dāng)循環(huán)正在運行時,開始執(zhí)行此循環(huán)已被編譯的版本。換句話說,當(dāng)循環(huán)的代碼被編譯完成,若 JVM 替換了代碼(前棧),那么循環(huán)的下個迭代執(zhí)行最新的被編譯版本則會更加快。
標(biāo)準(zhǔn)編譯是被-XX:CompileThreshold=Nflag 的值所觸發(fā)。Client 編譯器模式下,N 默認(rèn)的值 1500,而 Server 編譯器模式下,N 默認(rèn)的值則是 10000。 client 編譯器和 server 編譯器在最終的性能上有很大的差別,很大程度上是因為編譯器在編譯一個特定的方法時,對于兩種編譯器可用的信息并不一樣。降低編譯閾值,尤其是對于 server 編譯器,承擔(dān)著不能使應(yīng)用程序運行達(dá)到最佳性能的風(fēng)險,但是經(jīng)過測試應(yīng)用程序我們也發(fā)現(xiàn),將閾值從 8000 變成 10000,其實有著非常小的區(qū)別和影響。
一個方法擁有編譯資格之后,會將其放入隊列排隊等待編譯,編譯并不會遵守先進先出的原則,而是哪一個方法的計數(shù)器高,哪一個方法首先進行編譯。
5)調(diào)優(yōu)手段
從優(yōu)化的角度講,最簡單的選擇就是使用 server 編譯器的分層編譯技術(shù),這將解決大約 90%左右的與編譯器直接相關(guān)的性能問題。最后,請保證代碼緩存的大小設(shè)置的足夠大,這樣編譯器將會提供最高的編譯性能。
JVM內(nèi)存模型?

- 堆:存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存
- 虛擬機棧:虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候都會同時創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態(tài)鏈接、方法出口等信息
- 本地方法棧:本地方法棧則是為虛擬機使用到的Native方法服務(wù)。
- 方法區(qū):用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)
- 程序計數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器
GC垃圾回收?
引用計數(shù)法(存在循環(huán)引用的問題)和可達(dá)性算法(主流的JVM采用的是這種方式)