1、故障處理工具
基礎故障處理工具

jps:
可以列出正在運行的虛擬機進程,并顯示虛擬機執行主類(Main Class,main()函數所在的類)名稱以及這些進程的本地虛擬機唯一ID(LVMID,Local Virtual Machine Identifier)

jstat:
jstat(JVM Statistics Monitoring Tool)是用于監視虛擬機各種運行狀態信息的命令行工具

jinfo:
jinfo(Configuration Info for Java)的作用是實時查看和調整虛擬機各項參數,windos使用該命令會受到限制

jmap:
jmap(Memory Map for Java)命令用于生成堆轉儲快照,jinfo命令一樣,jmap有部分功能在Windows平臺下是受限的

jhat:
JDK提供jhat(JVM Heap Analysis Tool)命令與jmap搭配使用,來分析jmap生成的堆轉儲快照。可以通過HTTP界面可視化
.java文件不可以直接被jhat分析我們需要先調用以下代碼,轉換文件格式

jstack:
jstack(Stack Trace for Java)命令用于生成虛擬機當前時刻的線程快照(一般稱為threaddump或者javacore文件)。

2、類文件結構
class文件是一組以8個字節為基礎單位的二進制流,各個數據項目嚴格按照順序緊湊的排列在文件之中,中間沒有添加任何分隔符。
魔數和版本號
????????Class文件的魔數是確定這個文件是否為一個能被虛擬機接收的Class文件——取值為0xCAFEBABE
????????版本號分為主版本號和次版本號,高版本可以向下兼容低版本。虛擬機完全拒絕執行超過其版本號的Class文件
常量池
????????常量池存儲:字面量和符號引用。常量池的每一個常量都是一個表
訪問標志
????????這個標志用于識別一些類或接口層次的訪問信息。
類索引、父類索引、接口索引集合
????????class文件中有這三項數據來確定該類型的繼承關系
字段表集合
????????用于描述接口或類中聲明的變量
方法表集合
屬性表集合
3、類加載
類加載:JVM把類的數據從Class文件加載到內容,并對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型
類加載的時機
類的生命周期可以被定義為以下七個過程

加載——驗證——準備——初始化——卸載順序是確定的
有且僅有六種情況需要進行初始化(主動引用)
- 遇到 new、getstatic、putstatic 或 invokestatic 這四條字節碼指令時
- 典型場景:使用 new 關鍵字實例化對象、讀取或設置一個類的靜態字段(被 final 修飾、已在編譯期把結果放入常量池的靜態字段除外)、調用一個類的靜態方法
- 使用 java.lang.reflect 包的方法對類進行反射調用的時候
- 當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化
- 當虛擬機啟動時,用戶需要指定一個要執行的主類(包含 main () 方法的那個類),虛擬機會先初始化這個主類
- 當使用 JDK 7 新加入的動態語言支持時
- 當一個接口中定義了 JDK 8 新加入的默認方法(被 default 關鍵字修飾的接口方法)時,如果有這個接口的實現類發生了初始化,該接口要在其之前被初始化
除此之外,所有引用類型的方式都不會觸發初始化,稱為被動引用。
給個例子
package org.fenixsoft.classloading;
/*** 被動使用類字段演示一:* 通過子類引用父類的靜態字段,不會導致子類初始化**/
public class SuperClass {static {System.out.println("SuperClass init!");}public static int value = 123;
}
public class SubClass extends SuperClass {static {System.out.println("SubClass init!");}}
/*** 非主動使用類字段演示**/
public class NotInitialization {public static void main(String[] args) {System.out.println(SubClass.value);}
}
????????上述代碼運行之后,只會輸出“SuperClass init!”,而不會輸出“SubClass init!”。對于靜態字段,只有直接定義這個字段的類才會被初始化,因此通過其子類來引用父類中定義的靜態字段,只會觸發父類的初始化而不會觸發子類的初始化。
類的加載過程
加載、驗證、準備、解析、初始化
加載過程
????????通過類的全限定名獲得字節流
????????將字節流(靜態)轉換為方法區的運行時數據結構
????????在內存生成該對象作為方法區這個類的訪問接口
驗證
????????目的是確保Class文件的字節流中包含的信息符合《Java虛擬機規范》的全部約束要求。保證這些能正確地存儲到方法區。
文件格式驗證:保證輸入的字節流能正確地解析并存儲于方法區之內
元數據驗證

字節碼驗證

符號引用驗證

準備
只準備空間,這里的 初始值默認賦該類型的零值,若類變量被 final 修飾且為基本類型或字符串,則會在準備階段初始化為指定值,靜態常量直接賦值
解析
把符號引用直接轉為直接引用的過程
初始化
把準備階段賦的0值,轉換為實際數據
使用
卸載
類加載器
????????任意一個類,都必須由加載它的類加載器和這個類本身一起共同確立其在Java虛擬機中的唯一性,換句話說只有同一個類加載器加載出來的兩個相同的類才相等。
類加載器分類
- 啟動類加載器Bootstrap ClassLoader:加載固定路徑下,固定JRE/lib下的核心類庫(如rt.jar)
- 擴展類加載器Extension ClassLoader:加載JRE/lib/ext下的擴展類,是對Java語言的擴展
- 應用程序類加載器Application ClassLoader:加載用戶類路徑(ClassPath)上的類
- 自定義ClassLoader:用戶自定義的類加載器
雙親委派模型
雙親委派
雙親委派模型的工作流程:
????????如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到最頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它的搜索范圍中沒有找到所需的類)時,子加載器才會嘗試自己去完成加載
- 類加載請求先委托父加載器處理
- 父加載器無法完成時子加載器才嘗試加載
優點:
- 避免重復加載
- 防止核心API被篡改
破壞雙親委派
破壞雙親委派,指的是類加載器在某些場景下不遵循 “先委托父類加載器” 的規則。例如:
Tomcat 的類加載器設計
????????Tomcat 需要支持多個 Web 應用隔離(同一類在不同應用中可加載不同版本),因此其類加載器結構也破壞了雙親委派。
Tomcat 的類加載順序(以 WebAppClassLoader 為例):
- 先加載 Web 應用的/WEB-INF/classes目錄下的類;
- 再加載/WEB-INF/lib目錄下的 JAR 包;
- 最后才委托父類加載器(Common ClassLoader)加載。