【隨筆】JVM核心:JVM執行和類載入

科技小先鋒發表於2017-11-22

前言

本篇部落格將寫一點關於JVM的東西,涉及JVM執行時資料區、類載入的過程、類載入器、ClassLoader、雙親委派機制、自定義類載入器等,這些都是博主自己的一點理解,如果有誤,歡迎大家評論拍磚~

關於JVM執行時資料區

【隨筆】JVM核心:JVM執行和類載入

關於類載入

class檔案載入至記憶體,連結(校驗、解析),初始化;最終形成JVM可以直接使用的JAVA型別的過程。

載入:在方法區形成類的執行時資料結構;在堆裡面形成該類的Class物件,作為訪問方法區的入口。

【隨筆】JVM核心:JVM執行和類載入

連結:class檔案是否存在問題;一些符號引號替換成直接引用。

初始化:初始化一個類,先初始化它的父類。虛擬機器會保證一個類的初始化在多執行緒環境中被正確加鎖和同步。

要使用類A,必須先載入類A;載入類A,就會把靜態變數、靜態塊合併初始化,然後在呼叫構造器。注意類的載入和初始化,只有一次。

關於類載入器

上文已經說了,類載入器的作用就是:將class檔案的位元組碼內容載入到記憶體中,並將這些靜態資料轉化成方法區中的執行時資料結構,在堆中生成一個代表這個類的Class物件,作為方法區類資料的訪問入口。

類載入器的層次結構

引導類載入器bootstrap classloader

載入JAVA核心庫($JAVA_HOME/jre/lib/rt.jar),原生程式碼實現(C++),並不繼承自java.lang.ClassLoader。

擴充套件類載入器extensions classloader

JAVA可以提供一個擴充套件目錄($JAVA_HOME/jre/ext/*.jar)來載入Java類。

由sun.misc.Launcher.ExtClassLoader實現

應用程式類載入器application classloader(也稱系統類載入器)

一般來說,JAVA應用的類由它載入,即載入路徑是classpath下的路徑。

由sun.misc.Launcher.AppClassLoader實現。

自定義類載入器

開發人員繼承java.lang.ClassLoader實現自己的類載入器

【隨筆】JVM核心:JVM執行和類載入

關於java.lang.ClassLoader

ClassLoader的基本職責就是:

第一,根據指定的類名稱,找到或者生成對應的位元組碼,並根據位元組碼生成class物件

第二,載入JAVA應用所需的資源,如配置檔案等。

ClassLoader的組合模式

【隨筆】JVM核心:JVM執行和類載入

【隨筆】JVM核心:JVM執行和類載入

引導類載入器是原生程式碼實現,我們獲取不到,所以是null。

ClassLoader重要API

getParent():該類載入器的父類載入器

loadClass(String name):載入名稱為name的類,並返回Class例項。

載入順序是:先交給擴充套件類載入器載入,如果載入不到,交給引導類載入器載入,載入不到,交給自己去載入,如果自己也載入不到,那麼ClassNotFoundException。【雙親委派機制】 如果要改變類的載入順序,那麼可以override該方法。

findClass(String name),不是載入,僅僅是查詢而已

findLoadedClass(String name),查詢已經被載入過的

defineClass(String name,byte[] b, int off ,int len),可以把位元組陣列的內容轉換成JAVA類,並會返回Class例項。

類載入器的代理模式:雙親委派機制

類載入器的代理模式:就是把載入指定類的過程交給其他載入器。

JAVA預設使用的類載入器代理模式是:雙親委派機制。

雙親委派機制:

就是某個特定的類載入器接到載入類的請求時,首先將載入任務委託給父類載入器,依次追溯,比如說從應用載入器委託給擴充套件類載入器,從擴充套件類載入器委託給引導類載入器。這種委託,直至委託到層次最高的類載入器,即引導類載入器,如果委託的父類載入器可以完成載入任務,那麼成功返回;只有父類載入器無法完成時,才去自己載入。

可以看出雙親委派機制的意思就是優先父類載入器載入!

試想如果我們定義了一個java.lang.String類,根據雙親委派機制,那麼JDK只會載入它自己的String。這顯然保證了Java核心庫的型別安全。

雙親委派機制不是唯一的選擇

雖然JDK預設的類載入機制是雙親委派機制,但是並不是所有都採用,比如有些伺服器,如Tomcat,雖然也採用代理的方式載入,但是載入順序卻恰恰和雙親委派機制相反,它是首先嚐試載入這個類,只有載入不到的情況下,才去讓父類載入器代理載入。

為什麼會這樣呢,不是說雙親委派很安全麼?

其實就是在安全,和靈活方面進行取捨!

寫一個自定義類載入器

MyClassLoader:

【隨筆】JVM核心:JVM執行和類載入

重寫findClass:

【隨筆】JVM核心:JVM執行和類載入

Test:

【隨筆】JVM核心:JVM執行和類載入

一般情況下,自定義類載入器,需要繼承自ClassLoader。

首先來說,可以檢查請求的類是否已經被自定義的類載入器載入;如果載入了,那麼直接返回;否則,那麼交給父類載入器,就是進行雙親委派;如果雙親委派也載入不到,那麼交給自定義類載入器進行“自定義的方式”來載入類。

另外,被2個不同的類載入載入的同一個類,JVM不會認為是一個類。

本文轉自zfz_linux_boy 51CTO部落格,原文連結:http://blog.51cto.com/zhangfengzhe/2061298,如需轉載請自行聯絡原作者


相關文章