這導致class的**五花八門,而且存在安全隱患,試想一下,如果有人製作惡意class檔案
jvm虛擬機器照單全收,輕則導致電腦宕機,重則直接造成財產損失,這顯然是不被允許的
所以jvm自身的第一道防火牆,驗證工作就開始了,一共四個步驟,檔案格式驗證,元資料驗證,位元組碼驗證,符號引用驗證
檔案格式很好理解,就比如我們買了乙個拼裝玩具,如果出現了乙個拼裝說明書上沒有的零件,我們不得不懷疑是不是商家把零件搞混了,這種情況下,我們的第一反應肯定是去退貨
看看說明書是不是屬於同系列的新玩具(檢查當前class檔案的主副版本),最後看看零件是否齊全(檢查常量池,方法表,字段表的各種標誌位,然後結合上下文判斷型別/數量是否異常,如,方法表標誌位與方法數量不符,常量池是否有常量指向不存在的常量,標誌位異常值)
元資料就是類本身的資料,包含類的繼承,實現的介面,jvm在此主要檢測本類是否與父類衝突,例如,繼承了父類find修飾的方法,非法的過載(方法引數一致,但是返回值不同)
我在之前的文章中分析過一些簡單的位元組碼,但是顯然我們平時開發的專案並沒有那麼簡單,可能涉及幾十個類,幾百個方法,各種呼叫層層疊疊
這顯然是極端複雜的情況,而且這還涉及計算機中很著名的停機問題
什麼是停機問題呢,用我的話來說,假設你有一段全知全能的**,它可以判斷所有**是否可以完成(即停機),那麼就有以下悖論
if(全知全能的**)
while(true){}
else
return;
即,如果全知全能的**認為程式可以停止,那麼程式就不能停止,反之程式就可以停止,藉由這個理論,可以得出永遠無法用程式來判斷程式是否正確
位元組碼的判斷也是如此,對於元資料,和檔案格式驗證,我們可以判斷他們是否符合某種格式,因為他們是固定不變的
但是對於可變(這裡的可變是指可以隨著條件的改變,可以有截然不同的結果)的位元組碼來說,顯然想要判斷出他們是否正確是乙個偽命題,我們可以使用複雜的過程的將程式錯誤的機率降至最低
但是這顯然會影響到程式執行的效率,比如jvm曾經採用的型別推導來檢測
jvm最終採取了乙個折中的辦法,通過給方法表中有code屬性的方法新增乙個叫stackmaptable的屬性,該屬性記錄了程式執行時的各種狀態
相當於給程式拍一張**,檢查**相比檢查可以換衣服,帶假髮的真人顯然要簡單快速的多
、jvm因此可以節省大量用來檢測位元組碼的時間,但是有得必有失,理論上講,黑客也可以拍張假照來糊弄jvm
所以我們需要注意即使經過驗證,仍然有存在惡意class的可能性
符號引用的驗證,就和他的名字一樣,主要是驗證一下符號引用,但是這裡又有些不同
在前面檔案格式的驗證中,類自身的符號引用已經在檔案格式驗證中完成了
此處主要驗證的是本類對其他類引用是否合法,如是否存在,是否可以被本類訪問(如,引用的是private修飾的型別)
大戰Java虛擬機器 3 類載入機制
當你的 編譯成class檔案之後,那麼虛擬機器如何載入這些檔案呢?我們需要知道虛擬機器到底做了什麼樣的事情。載入 鏈結 初始化 使用 解除安裝 1 載入 讀取二進位制位元組流,將靜態儲存結構轉化為方法區執行時的資料結構,在記憶體中生詞乙個代表這個類的物件。2 驗證 驗證格式,元資料,位元組碼,符合引...
Java虛擬機器載入類的順序
虛擬機器載入類的先後順序和特性 虛擬機器載入類的時候,1.先載入該類的靜態常量和變數 靜態常量和變數按順序載入 2.然後載入靜態 塊,3.最後再載入其它部分。而且,靜態常量和靜態 塊只在載入類的時候執行一次,new 新的物件的時候不再執行.public class statictest public...
類在虛擬機器中的載入流程
載入 連線 驗證 準備 解析 初始化 1 載入 通過類的全名獲取類的二進位製流,將類的資料結構轉化為方法區的結構,在記憶體中生成乙個代表這個類的class物件 2 連線 驗證 確保class資訊正確無害 準備 在方法區初始化變數的初始值,物件為null static int a 1 初始化為0 st...