一、JVM內存結構
虛擬機棧:存放基本數據類型、對象引用、方法出口等;
本地方法棧:服務於本地方法,線程私有
堆:java內存最大的一塊,所有對象實例、數組都存放在java堆,GC回收的地方,線程共享;
方法區:存放已被加載的類信息、常量、靜態變量、既編譯器編譯後的代碼數據等;
程序計數器:當前線程所執行字節碼的行號指示器,用於記錄正在執行的虛擬機字節碼指令地址上,線程私有;
二、新生代、老年代模型
默認新生代與老年代比例爲 1:2,可以通過-XX:NewRatio配置;
默認Edem:S0:S1 = 8:1:1,可以通過參數-XX:SurvivorRatio配置;
Survivor區中的對象被複制15次,可以晉升,對應虛擬機參數-XX:+MaxTenuringThreshold;
三、爲什麼要分Eden和Survivor?爲什麼要設置兩個Survivor區?
如果沒有Survivor區,Eden區每進行一次MinorGC,存活的對象就會被送到老年代。
老年代滿了後,會觸發MajorGC,老年代的內存空間遠大於新生代,進行一次FullGC消耗的時間比MinorGC長的多,所以需要分爲Eden和Suevivor區。
設置兩個Survivor區,最大的好處是解決了內存碎片化,採用複製清除算法,保證了清除後內存是連續的,避免了碎片化的發生。
四、JVM中一次完整的GC流程是怎麼樣的,如何晉升到老年代?
- 對象先存儲在Eden區,當Eden區滿了後,Java虛擬機會觸發一次MinorGC,以收集新生代的垃圾,存活下來的對象,則會轉移到Survivor區。
- 大對象(需要大量連續內存空間的對象)直接進入老年代。
- 如果對象在Eden出生,並且經過第一次MinorGC後仍存活,並且被Survivor容納的話,年齡設爲1,每熬過一次MinorGC,年齡+1,年齡超過一定限制(默認15),則被晉升到老年態,即長期存活的對象進入到老年代。
- 老年代滿了後而無法容納更多的對象,MinorGC之後通常會進行FullGC,FullGC清理整個內存堆,包括年輕代和年老代。
- MajorGC發生在老年代的GC,清理老年代,經常會伴隨至少一次MinjorGC,比MinorGC慢10倍以上。
五、有哪幾種垃圾回收器?各自的優缺點?
- Serial New收集器:單線程收集器,收集垃圾時,會停止其他任務的執行,複製算法;
- Parallel New收集器:多線程收集器,複製算法;
- Servial Old收集器:單線程收集器,標記整理算法;
- Parallel Old收集器:多線程收集器,標記整理算法;
- CMS收集器:是一種以獲得最短回收停頓時間爲目標的收集器,標記清除算法。運作過程:初始標記 、併發標記、重新標記、併發清除,收集結束會產生大量內存碎片;
- G1收集器:標記整理算法實現,運作流程如下:初始標記、併發標記、最終標記、篩選標記,不會產生內存碎片,可以精確地控制停頓;
六、CMS收集器和G1收集器的區別?
- CMS收集器是老年代的收集器,可以配合新生代的Serival和Parallal收集器一起使用;
- G1收集器收集範圍是新生代和老年代,不需要結合其他收集器使用;
- CMS收集器以最小停頓時間爲目標的收集器;
- G1收集器可預測垃圾回收的停頓時間;
- CMS收集器是使用“標記-清除”算法進行的垃圾回收,容易產生內存碎片;
- G1收集器是使用“標記-整理”算法,不會產生內存碎片。
七、JVM內存模型
- Java內存模型規定了所有的變量都存儲在主內存中,每條線程還有自己的工作內存,線程的工作內存中保存了該線程使用到的變量的主內存副本拷貝,線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存。不同的線程無法直接訪問對方工作內存中的變量,線程間變量的傳遞均需要自己的工作內存和主內存直接進行數據同步進行。
八、volatile關鍵字
- 使用volatile關鍵字,保證了變量的可見性,防止指令重排,打破了內存屏障,當線程在使用變量的時候,都會從主內存中取新的變量值。
九、類加載器
BootStrap ClassLoader:加載<JAVA_HOME>\lib 目錄下的類
Ext ClassLoader:加載<JAVA_HOME>\lib\ext 目錄下的類
Application ClassLoader:加載classpath上的指定類庫,默認使用這個類加載器
十、雙親委派模型
- 如果一個類加載器收到類加載的請求,它首先不會自己嘗試加載這個類,而是把這個類委派給父類加載器加載。每個類加載器都是如此,只有當父類加載器在自己的搜索範圍內找不到指定的類時,子加載器才嘗試自己去加載。
十一、爲什麼要雙親委派機制?
- 如果沒有雙親委派,用戶自己定義一個java.lang.Object同名類,並把它放在ClassPath中,那麼類之間的比較結果以及唯一性將無法保證。當有雙親委派機制後,可以防止內存中出現多份同樣的字節碼。
十二、如何打破雙親委派模型?
- 打破雙親委派機制不僅要繼承ClassLoad類,還需要重寫loadClass方法和findClass方法。
十三、JVM調優工具
- Jconsole:用於堆JVM中的內存、線程和類等進行監控。
- Jstatic:用於查看線程信息,例如查看死鎖問題。
- Jmap:查看堆內存使用情況,導出Java進程的內存快照文件,一般採用MAT工具分析文件。
十四、JVM調優目標
- 減少全局變量和大對象。
- 調整新生代的大小。
- 設置老年代的大小。
- 選擇合適的GC收集器。
十五、JVM常用參數
- -Xms:初始堆大小
- -Xmx:最大堆大小
- -XX:NewRatio 設置新生代和老年代的比例
- -XX:SurviorRatio Eden和S0、S1的比值
- -XX:+UseG1GC 使用G1收集器
十六、BTrace,Java線上問題排查神器
解決了什麼問題?
- 哪些方法慢,例如監控執行時間超過1S的方法
- 查看哪些方法調用了System.gc()
- 查看方法參數或對象屬性
- 執行某個方法拋出異常時,分析運行時參數
使用限制:
- 不能創建對象
- 不能使用數組
- 不能拋出或捕獲異常
- 不能使用循環
- 不能使用synchronized關鍵字
- 屬性和方法必須使用static修飾
十七、類加載過程
加載->驗證->準備->解析->初始化
十八、CMS運行原理
- 初始標記:會stop the world停止工作線程,速度快。
- 併發標記:系統一邊工作,一邊進行垃圾回收,工作期間會陸續有對象進入老年代,很耗時。
- 併發清理:和系統併發運行,不影響工作。清理掉被標記的垃圾對象,並移動整理。
十九、G1運行原理
- 初始標記
- 併發標記
- 最終標記
- 清楚標記
二十、強軟弱虛使用場景
- 強引用:正常的創建對象,只要引用存在,永遠不會被GC回收,即使OOM。
- 軟引用:內存溢出之前進行回收,GC時內存不足時回收,如果內存足夠就不回收。
- 弱引用:每次GC時進行回收,無論內存是否足夠。