中高級面試必備,學會JVM調優對與Java工程師來說非常的重要,不管是實際項目中遇到問題,還是面試中,都需要這一塊的知識,小編由簡入深的,給大家分享一波。
❞
1.JDK、JRE、JVM關係?
Jdk (Java Development Kit) : java語言的軟件開發包。包括Java運行時環境Jre。
Jre (Java Runtime Environment) :Java運行時環境,包括Jvm。
Jvm (Java Virtual Machine) :
-
一種用於計算機設備的規範。
-
Java語言在不同平臺上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行。
Jdk包括Jre,Jre包括jvm。
2.啓動程序如何查看加載了哪些類,以及加載順序?
java -XX:+TraceClassLoading 具體類
Java -verbose 具體類
3. class字節碼文件10個主要組成部分?
-
MagicNumber
-
Version
-
Constant_pool
-
Access_flag
-
This_class
-
Super_class
-
Interfaces
-
Fields
-
Methods
-
Attributes
4.畫一下jvm內存結構圖?
https://user-gold-cdn.xitu.io/2020/5/13/1720da9b21047968?imageView2/0/w/1280/h/960/ignore-error/1
5.程序計數器
屬於線程私有內存。佔用一塊非常小的空間,它的作用可以看作是當前線程所執行的字節碼的行號指示器。字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的指令的字節碼,分支、循環、跳轉、異常處理、線程恢復等基礎功能都依賴這個計數器來完成。
6.Java虛擬機棧
屬於線程私有內存。它的生命週期與線程相同,虛擬機棧描述的是Java方法執行內存模型;每個方法被執行的時候都會同時創建一個棧楨用於存儲局部變量表、操作棧、動態鏈接、方法出口信息等。每一個方法被調用直至執行完成的過程,就對應着一個棧幀再虛擬機中從入棧到出棧的過程。
7.本地方法棧
本地方法棧與虛擬機棧所發揮的作用是非常相似的,只不過虛擬機棧對虛擬機執行Java方法服務,而本地棧是爲虛擬機使用到Native方法服務。
8.Java堆
是Java虛擬機所管理的內存中最大的一塊。Java堆事被所有線程共享的一塊內存區域,在虛擬機啓動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。
Tips:但隨着JIT編譯器的發展與逃逸分析技術的逐漸成熟,棧上分配、標亮替換優化技術將會導師一些微妙的變化發生,所有的對象都分配在堆上就不那麼絕對了。
9.方法區
是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
10.運行時常量池?
是方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息,還有一項是常量池(Constant PoolTable)用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後存放道方法區的運行時常量池中。
11.什麼時候拋出StackOverflowError?
如果線程請求的棧深度大於虛擬機所允許的深度,則拋出StackOverflowError。
12.Java7和Java8在內存模型上有什麼區別?
Java8取消了永久代,用元空間(Metaspace)代替了,元空間是存在本地內存(Native memory)中。
13.程序員最關注的兩個內存區域?
堆(Heap)和棧(Stack),一般大家會把Java內存分爲堆內存和棧內存,這是一種比較粗糙的劃分方式,但實際上Java內存區域是很複雜的。
14.直接內存是什麼?
直接內存並不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域,但這部分內存也頻繁被實用,也有OutOfMemoryError異常的出現的可能。
Tips:JDK1.4中加入了NIO(new input/output)類,引入了一種基於通道(Channe)與緩衝區(Buffer)的I/O方式,也可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆裏面的DirectByteBuffer的對象作爲這塊內存的引用進行操作。
15.除了哪個區域外,虛擬機內存其他運行時區域都會發生OutOfMemoryError?
程序計數器。
16.什麼情況下會出現堆內存溢出?
堆內存存儲對象實例。我們只要不斷地創建對象。並保證gc roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象。就會在對象數量到達最大。堆容量限制後,產生內存溢出異常。
17.如何實現一個堆內存溢出?
public class Cat {
public static void main(String[] args) {
List list = new ArrayList();
while (true) {
list.add(new Cat());
}
}
}
18.空間什麼情況下會拋出OutOfMemoryError?
如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError。
19.如何實現StrackOverflowError?
public static void main(String[] args) {
eat();
}
public static void eat () {
eat();
}
20.如何設置直接內存容量?
通過 -XX:MaxDirectMemorySize指定,如果不指定,則默認與Java堆的最大值一樣。
21.Java堆內存組成?
https://user-gold-cdn.xitu.io/2020/5/13/1720da9b1f827a21?imageView2/0/w/1280/h/960/ignore-error/1
堆大小 = 新生代 + 老年代。如果是Java8則沒有Permanent Generation。
其中新生代(Young) 被分爲 Eden和S0(from)和S1(to)。
22.Edem : from : to默認比例是?
Edem : from : to = 8 : 1 : 1
此比例可以通過 –XX:SurvivorRatio 來設定
23.垃圾標記階段?
在GC執行垃圾回收之前,爲了區分對象存活與否,當對象被標記爲死亡時,GC纔回執行垃圾回收,這個過程就是垃圾標記階段。
24.引用計數法?
比如對象a,只要任何一個對象引用了a,則a的引用計數器就加1,當引用失效時,引用計數器就減1,當計數器爲0時,就可以對其回收。
但是無法解決循環引用的問題。
25.根搜索算法?
跟搜索算法是以跟爲起始點,按照從上到下的方式搜索被根對象集合所連接的目標對象是否可達(使用根搜索算法後,內存中 的存活對象都會被根對象集合直接或間接連接着),如果目標對象不可達,就意味着該對象己經死亡,便可以在 instanceOopDesc的 Mark World 中將其標記爲垃圾對象。
在根搜索算法中, 只有能夠被根對象集合直接或者間接連接的對象纔是存活對象。
26.JVM中三種常見的垃圾收集算法?
標記-清除算法(Mark_Sweep)
複製算法(Copying)
標記-壓縮算法(Mark-Compact)
27.標記-清除算法?
首先標記出所有需要回收的對象,在標記完成後統一回收掉所有的被標記對象。
https://user-gold-cdn.xitu.io/2020/5/13/1720da9b1f96fa42?imageView2/0/w/1280/h/960/ignore-error/1
缺點:
-
標記和清除的效率都不高。
-
空間問題,清除後產生大量不連續的內存隨便。如果有大對象會出現空間不夠的現象從而不得不提前觸發另一次垃圾收集動作。
28.複製算法?
他將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊,當這一塊內存用完了,就將還存活的對象複製到另外一塊上面,然後再把已使用過的內存空間一次清理掉。https://user-gold-cdn.xitu.io/2020/5/13/1720da9b212095d3?imageView2/0/w/1280/h/960/ignore-error/1
優點:
解決了內存碎片問題。
缺點:
將原來的內存縮小爲原來的一半,存活對象越多效率越低。
29.標記-整理算法?
先標記出要被回收的對象,然後讓所有存活的對象都向一端移動,然後直接清理掉邊界以外的內存。解決了複製算法和標記清理算法的問題。
https://user-gold-cdn.xitu.io/2020/5/13/1720da9b4fb309b4?imageView2/0/w/1280/h/960/ignore-error/1
30.分代收集算法?
當前商業虛擬機的垃圾收集都採用“分代手機算法”,其實就根據對象存活週期的不同將內存劃分爲幾塊,一般是新老年代。根據各個年代的特點採用最適當的收集算法。
31.垃圾收集器?
如果說垃圾收集算法是方法論,那麼垃圾收集器就是具體實現。連線代表可以搭配使用。
32.Stop The World?
進行垃圾收集時,必須暫停其他所有工作線程,Sun將這種事情叫做"Stop The World"。
33.Serial收集器?
單線程收集器,單線程的含義在於它會 stop the world。垃圾回收時需要stop the world ,直到它收集結束。所以這種收集器體驗比較差。
34.PartNew收集器?
Serial收集器的多線程版本,除了使用採用並行收回的方式回收內存外,其他行爲幾乎和Serial沒區別。
可以通過選項“-XX:+UseParNewGC”手動指定使用 ParNew收集器執行內存回收任務。
36.Parallel Scavenge?
是一個新生代收集器,也是複製算法的收集器,同時也是多線程並行收集器,與PartNew 不同是,它重點關注的是程序達到一個可控制的吞吐量(Thoughput,CPU 用於運行用戶代碼 的時間/CPU 總消耗時間,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)), 高吞吐量可以最高效率地利用 CPU 時間,儘快地完成程序的運算任務,主要適用於在後臺運算而不需要太多交互的任務。
他可以通過2個參數精確的控制吞吐量,更高效的利用cpu。
分別是: -XX:MaxGcPauseMillis 和 -XX:GCTimeRatio
37.Parallel Old收集器?
Parallel Scavenge收集器的老年代版本,使用多線程和標記-整理算法。JDK 1.6中才開始提供。
38.CMS 收集器?
Concurrent Mar Sweep 收集器是一種以獲取最短回收停頓時間爲目標的收集器。重視服務的響應速度,希望系統停頓時間最短。採用標記-清除的算法來進行垃圾回收。
39.CMS垃圾回收的步驟?
-
初始標記 (stop the world)
-
併發標記
-
重新標記 (stop the world)
-
併發清除
初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快。
併發標記就是進行Gc Roots Tracing的過程。
重新標記則是爲了修正併發標記期間,因用戶程序繼續運行而導致的標記產生變動的那一部分對象的標記記錄,這個階段停頓時間一般比初始標記時間長,但是遠比並發標記時間短。
整個過程中併發標記時間最長,但此時可以和用戶線程一起工作。
41.CMS收集器優點?缺點?
優點:
併發收集、低停頓
缺點:
-
對cpu資源非常敏感。
-
無法處理浮動垃圾。
-
內存碎片問題。
42.G1收集器?
Garbage First 收集器是當前收集器技術發展的最前沿成果。jdk 1.6_update14中提供了 g1收集器。
G1收集器是基於標記-整理算法的收集器,它避免了內存碎片的問題。
可以非常精確控制停頓時間,既能讓使用者明確指定一個長度爲 M毫秒的時間片段內,消耗在垃圾收集上的時間不多超過N毫秒,這幾乎已經是實時java(rtsj)的垃圾收集器特徵了。
42. G1收集器是如何改進收集方式的?
極力避免全區域垃圾收集,之前的收集器進行收集的範圍都是整個新生代或者老年代。而g1將整個Java堆(包括新生代、老年代)劃分爲多個大小固定的獨立區域,並且跟蹤這些區域垃圾堆積程度,維護一個優先級李彪,每次根據允許的收集時間,優先回收垃圾最多的區域。從而獲得更高的效率。
43.虛擬機進程狀況工具?
jps (Jvm process status tool ),他的功能與ps類似。
可以列出正在運行的虛擬機進程,並顯示執行主類(Main Class,main()函數所在的類)的名稱,以及浙西進程的本地虛擬機的唯一ID。
語法 :jps [options] [hostid]
-q 主輸出lvmid,省略主類的名稱
-m 輸出虛擬機進程啓動時傳遞給主類main()函數的參數
-l 輸出主類全名,如果進程執行是Jar包,輸出Jar路徑
-v 輸出虛擬機進程啓動時JVM參數
44.虛擬機統計信息工具?
jstat(JVM Statistics Montoring Tool)是用於監視虛擬機各種運行狀態信息命令行工機具。他可以顯示本地或遠程虛擬機進程中的類裝載、內存、垃圾收集、jit編譯等運行數據。
jstat [option vmid [interval[s|ms] [count]] ]
interval 查詢間隔
count 查詢次數
如果不用這兩個參數,就默認查詢一次。
option代表用戶希望查詢的虛擬機信息,主要分3類:
-
類裝載
-
垃圾收集
-
運行期編譯狀況
45.jstat 工具主要選項?
-class 監視類裝載、卸載數量、總空間及類裝載鎖消耗的時間
-gc 監視Java堆狀況,包括Eden區,2個survivor區、老年代
-gccapacity 監視內容與-gc基本相同,但輸出主要關注Java堆各個區域使用的最大和最小空間
-gcutil 監視內容與-gc基本相同,主要關注已經使用空間站空間百分比
-gccause 與-gcutil 功能一樣,但是會額外輸出導致上一次GC產生的原因
-gcnew 監視新生代的GC的狀況
-gcnewcapacity 監視內容與 -gcnew基本相同,輸出主要關注使用到的最大最小空間
-gcold 監視老年代的GC情況
-gcoldcapacity 監控內容與 -gcold基本相同,主要關注使用到的最大最小空間
-compiler 輸出jit 編譯器編譯過的方法、耗時等信息
45.配置信息工具?
jinfo(Configuration Info for Java)的作用是實時地查看和調整虛擬機的各項參數。
使用jps 命令的 -v 參數可以查看虛擬機啓動時顯示指定的參數列表。
jinfo 語法: jinfo [option] pid
46.內存映像工具?
jmap(Memory Map for Java) 命令用於生成堆轉儲快照(一般稱爲heapdump或dump文件)。
語法 :jmap [option] vmid
它還可以查詢finalize執行隊列,Java堆和永久代的詳細信息,如果空間使用率、當前用的是哪種收集器等。
-
-dump 生成Java堆轉儲快照,其中live自參數說明是否只dump出存活對象
-
-finalizerinfo 顯示在F -Queue 中等待Finalizer線程執行finalize方法的對象。只在Linux/Solaris平臺下有效
-
-heap 顯示Java堆詳細信息,如使用哪種回收器、參數配置、分代狀況。
-
-histo 顯示堆中對象統計信息、包括類、實例數量和合計容量。
-
-F 當虛擬機進程對-dump選項沒有響應時,可使用這個選項強制生成dump快照。
47.虛擬機堆轉存儲快照分析工具?
jhat ( JVM Heap Analysis Tool) 用來分析jmap生成的堆轉儲快照。
48.堆棧跟蹤工具?
jstack(Stack Trace for Java) 命令用於生成虛擬機當前時刻的線程快照(一般稱爲thread dump 或javacore文件)。線程快照就是當前虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因。
jstack [option] vmid
-F 當正常輸出的請求不被響應時,強制輸出線程堆棧
-l 除堆棧外,顯示關於鎖的附加信息
-m 如果調用本地方法的花,可以顯示C/C++ 的堆棧
49.除了命令行,還有什麼可視化工具?
JConsole 和 VisualVM,這兩個工具是JDK的正式成員。
50.類的生命週期?
image
51.類加載過程?
加載 -> 校驗 -> 準備 -> 解析 -> 初始化
52.鏈接階段分幾個步驟?
校驗 -> 準備 -> 解析
53.哪5種情況必須對類進行初始化?
1.遇到new、getstatic、putstatic、invokestatic 4個字節碼指令時。
2.使用java.lang.relect包的方法對類進行反射調用時。
3.當初始化一個類的時候,如果父類沒被初始化,則先初始化父類。
4.當虛擬機啓動時,指定了執行主類(main()的方法的那個類),則需要先初始化這個主類。
5.JDK1.7動態語言支持時,如果一個java.lang.invoke.MethodHandle實例最後解析結果REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,且該句柄對應的類沒有初始化,則需要先初始化這個類。
54.以下代碼會輸出?
public class TestStatic {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}
class SuperClass {
static {
System.out.println("Java小咖秀 super init");
}
public static int value = 123;
}
class SubClass extends SuperClass {
static {
System.out.println("Java小咖秀 sub init");
}
}
輸出:
Java小咖秀 super init
123
原因:對於static字段,只有直接定義這個字段的類纔會被初始化。
55.加載階段做哪些事情?
根據已知的class完全限定名,來獲取二進制classfile格式的字節流。
將這個字節流代表的靜態存儲結構轉化爲方法區的運行時數據結構。
在內存中生成一個代表這個類的java.lang.Class對象,作爲方法區這個類的各種數據的訪問入口。
56.驗證階段的意義?
確保Class文件中的字節流中包含的信息符合當前虛擬機的要求,不會危害虛擬機自身安全。當檢測到字節流不符合規範時,拋出對應的異常。
57.驗證大概分幾個階段?
-
文件格式驗證
-
元數據驗證
-
字節碼驗證
-
符號引用驗證
58.準備階段?
該階段是正式爲類變量分配內存並設置類變量初始值。強調一點,這裏說的類變量是指被static修飾的,而不是實例變量。
59.準備階段被final修飾的類變量是如何附值的?
public static final int value = 2333;
附執行的數值2333.
60.解析階段?
該階段虛擬機將常量池內的符號引用替換爲直接引用的過程。
61.初始化?
類或者類執行初始化方法,類變量真正被附值的階段。
62.類加載器有哪些?
-
啓動類加載器(BootstrapClassLoader)
-
擴展類加載器(ExtClassLoader)
-
應用類加載器(AppClassLoader)
63.啓動類加載器(BootstrapClassLoader)?
Bootstrap ClassLoader 啓動類加載器:負責加載Java核心類,用原生C++代碼來實現的,並不是繼承自java.lang.ClassLoader,跟加載器,它就不存在父類加載器的說法類。
負責加載下列三種類型核心類庫:
%JAVA_HOME%/jre/lib目錄。
-Xbootclasspath 參數所指定的目錄。
系統屬性 sun.boot.class.path 指定的目錄中特定名稱的jar包。
64.擴展類加載器(ExtClassLoader)?
Extension ClassLoader 擴展類加載器 : 負責加載擴展類。
加載下面兩種情況的指定類庫:
-
%JAVA_HOME%/jre/lib/ext目錄
-
java.ext.dirs 指定目錄中的所有類庫
65.應用類加載器(AppClassLoader)?
App ClassLoader 應用類加載器:負責加載應用程序類庫。
加載下面兩種指定類庫:
-
java命令-classpath、-cp選項
-
系統屬性java.class.path指定jar包和類路徑
66.可以自定義類加載器嗎?
可以,「自定義加載器」是以「App ClassLoader作爲父類」。
67.雙親委託機制?
當有類需要加載的時候,會逐級上找,比如自定義類加載器,先去問問自己的父類加載器AppClassLoader,AppClassLoader問ExtClassLoader,ExtClassLoader 問BootstrapClassLoader,比如啓動類已經加載類該類,其他的子加載器就不用加載了。如果父類加載器加載不了,子類纔會嘗試去加載。如果所有加載都加載不到該類,拋出ClassNotFountException 異常。
68.如何查看加載了那些類,順序?
java -XX:+TraceClassLoading com.xiaoka.HelloFriend