1. JVM
1.1 Java運行時數據區
1.1.1 線程私有區域
- 程序計數器:線程所執行的字節碼的行號指示器(唯一沒有oom異常的區域),記錄正在執行的虛擬機字節碼指令的地址(如果正在執行的是本地方法則爲空)。
- Java虛擬機棧:描述Java方法執行的內存模型。
- 本地方法棧:描述Native方法執行的內存模型。
1.1.2 線程共享區域
- 堆:存放所有的實例對象(-Xms設置堆最小值,-Xmx設置堆最大值)。
- 方法區(JDK8後的元空間):存儲已加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼以及運行時常量池。
- 直接內存: NIO類中引入了一種基於通道與緩存區 的 I/O 方式,它可以直接使用Native函數庫直接分配堆外內存,然後通過一個存儲在 Java 堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作。
1.2 內存分配與回收
1.2.1 內存分配
- 對象優先在Eden區分配。
- 大對象直接在老年代分配。
- 長期存活對象進入老年代。
1.2.2 對象訪問定位
- 句柄:Java 堆中會劃分出一塊內存來作爲句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息。
- 直接指針:Java 堆對象的佈局中就必須考慮如何放置訪問類型數據的相關信息,而 reference中存儲的直接就是對象的地址。
這兩種對象訪問方式各有優勢。使用句柄來訪問的最大好處是reference中存儲的是穩定的句柄地址,在對象被移動時只會改變句柄中的實例數據指針,而reference本身不需要修改。使用直接指針訪問方式最大的好處就是速度快,它節省了一次指針定位的時間開銷。
1.2.3 垃圾收集(GC)
1.2.3.1 哪些內存需要回收
- 引用計數算法(無法處理循環引用)
- 可達性分析算法
思想:從GC Roots開始分析引用鏈,不可達的對象可以回收。
GC Roots:虛擬機棧中引用的對象、本地方法棧中引用的對象、方法區中類靜態屬性引用的對象、方法區中常量引用的對象。
Java中的引用類型
強引用(new):只要強引用還存在,垃圾收集器就不會回收被引用的對象。
軟引用(SoftReference):在系統將要發生內存溢出異常之前,將會把這些對象列入回收範圍進行二次回收。
弱引用(WeakReference):只能存活到下次垃圾收集發生之前。
虛引用(PhantomReference):一個對象是否有虛引用存在,不對其生存時間構成影響。
拯救對象:可以用finalize()方法拯救一次對象。
1.2.3.2 垃圾回收算法
- 標記清除(先標記後清除):標記和清除步驟效率低,容易產生內存碎片。
- 複製算法(將內存分塊):每次只能使用一部分內存造成浪費,適合新生代(Eden:Survivor = 8:1)。
- 標記整理(標記移動清理):適合老年代。
- 分代收集算法:新生代和老年代採用不同回收算法。
1.2.3.3 垃圾收集器
- Serial/Serial Old收集器
- Parallel/Parallel Old收集器
- CMS(Concurrent Mark Sweep):初始標記、併發標記、重新標記、併發清除
- G1:初始標記、併發標記、最終標記、篩選回收
1.3 性能監控與故障處理工具
1.3.1 jps(Jvm Process Status):虛擬機進程狀態工具
jps的功能類似於Linux的ps命令,可以列出正在運行的虛擬機進程, 並顯示虛擬機執行主類名稱,以及這些進程的本地虛擬機唯一ID(LVMID)。
jps [options] [hostid]
options:
-l 輸出主類全名,如果執行的是jar包則輸出jar路徑
-v 輸出虛擬機進程啓動時JVM參數
hostid:開啓了RMI服務的遠程虛擬機主機名
1.3.2 jstat(Jvm Statistic):虛擬機統計信息監視工具
jstat用於監視虛擬機的各種運行狀態信息,包括類裝載、垃圾收集以及運行期編譯狀況等。
jstat [ option vmid [ interval[s|ms] [count] ] ]
interval與count代表查詢間隔和次數
vmid是虛擬機進程ID
option:
-gc:監視Java堆狀況,包括容量、已用空間以及GC信息等
-gcutil:與gc選項基本相同,但關注點主要是已使用空間佔總空間的百分比
1.3.3 jinfo(Configuration Info):Java配置信息工具
jinfo的作用是實時查看和調整虛擬機各項參數。
jinfo [option] pid
1.4 類加載機制
1.4.1 類的生命週期
加載、連接(驗證、準備、解析)、初始化、使用、卸載
- 加載:通過一個類的全限定名來獲取定義此類的二進制字節流,將這個字節流所代表的的靜態存儲結構轉化爲方法區的運行時數據結構,在內存中生成一個代表這個類的
java.lang.Class
對象作爲方法區這個類的各種數據的訪問入口。 - 驗證:確保Class文件字節流中包含的信息符合當前虛擬機的要求,包括文件格式驗證、元數據驗證、字節碼驗證以及符號引用驗證。
- 準備:爲類變量分配內存並設置類變量初始值,這裏的類變量指static變量。
- 解析:將常量池內的符號引用替換爲直接引用。
- 初始化:準備階段變量已經賦過一次系統要求的初始值,初始化階段則根據程序員的要求進行變量初始化,即執行類構造器的
<clinit>()
方法。 - 使用
- 卸載
1.4.2 類加載器
類加載器在層次上由上至下(由父至子)分別爲:
- 啓動類加載器(Bootstrap):加載
%JAVA_HOME%\lib
目錄下的類庫。 - 擴展類加載器(Extension):加載
%JAVA_HOME%\lib\ext
目錄下的類庫。 - 應用程序類加載器(Applicaton):加載用戶類路徑上指定的類庫。
- 自定義類加載器(User):繼承ClassLoader,並覆蓋findClass()方法。
雙親委派模型:若一個類加載器收到類加載請求,它首先不會嘗試自己加載這個類,而是把這個請求委託給它的父加載器去完成,每一層次類加載器都是如此,只有當父類加載無法加載時,子加載器纔會嘗試自己加載。
破壞雙親委派機制:自定義類加載器,重寫loadClass()方法