我把JVM的類加載器整理了一下

前言

​ 以前去面試的時候面試官問了我關於關於JVM性能調優的問題,因爲本身以前公司的項目裏本身沒有接觸到JVM性能調優的相關問題(感受這些都是公司架構師考慮的問題),全部面試官問的時候本身一臉懵逼,全部最後的結果固然是涼涼。。,因而,爲了查漏補缺,就去學習了一下JVM的相關知識,但願能幫助到你們。java

正文

​ 在學習任何一項新的知識以前,我都會先列出一份學習大綱,而後按照這個學習大綱一步一步的來學習瞭解,因此學習JVM這個新的技術,我也分爲了3個板塊來學習:JVM類加載器,JVM內存結構,JVM垃圾回收這三個板塊來學習,今天這篇文章講的是JVM類加載器。程序員

一、什麼是JVM

​ 既然是學習關於JVM的相關理論知識,咱們固然得知道什麼是JVM。JVM是Java Virtual Machine(Java虛擬機)的縮寫。既然說到虛擬機,可能又會有人問什麼是虛擬機了,我這裏把虛擬機得相關概念放在這裏:面試

虛擬機:就是一臺虛擬的計算機,他是一款軟件;用來執行一系列計算機指令。虛擬機能夠分爲系統虛擬機和程序虛擬機。spring

  • 系統虛擬機:好比VMware,他們徹底是對物理計算機的仿真,提供了一個可運行完整操做系統的軟件平臺。數組

  • 程序虛擬機:好比Java虛擬機,它專門爲執行單個計算機程序而設計。在Java虛擬機中執行的 指令咱們稱爲Java字節碼指令。(JVM是運行在操做系統之上的,它與硬件沒有直接的交互)安全

因此根據定義,咱們能夠得知JVM是程序虛擬機。那麼JVM在哪裏呢,其實,咱們在最開始學習Java得時候,都必須按照Java得運行環境,從網上下載JDK安裝包,安裝完成以後,在安裝路徑下會有兩個文件夾,一個叫Jdk,一個叫jre,而java虛擬機就在jre的文件夾裏面。數據結構

​ 存在即有他存在的道理,那麼JVM的存在有什麼用呢?他是用來幹嗎的呢?學過JAVA的都知道,java程序要想運行,Java源程序(.java)要先編譯成與平臺無關的字節碼文件(.class),而後字節碼文件再解釋成機器碼運行。而解釋得這個過程就是經過Java虛擬機來執行的(能夠參考下面這張圖理解)。java虛擬機是來解釋字節碼文件的,而解釋得這個過程實際上是一個很複雜得過程,因此這就到了咱們今天要講得主題了。架構

二、類加載(classLoading)

​ 咱們先來了解一下類加載得整個過程。從下圖能夠看到類的生命週期一共分爲5個階段,加載、鏈接(包括驗證、準備和解析)、初始化、使用(類得實例化)、卸載(垃圾回收)。框架

​ 在Java代碼中,咱們都知道類(指的是類自己Class,好比,Interface,Enum)的加載、鏈接、初始化過程都是在程序運行期間完成的。下面咱們就先講一下類得加載、鏈接和初始化。jvm

類的加載:*最多見的一種狀況*是將已存在的類的Class文件(也就是字節碼文件)從磁盤上面加載到內存裏面,將其放在運行時數據區的方法區中,而後在內存中建立一個java.lang.Class對象用來封裝類在方法區中的數據結構

類的鏈接(又細分了三個階段):

​ 一、驗證:確保被加載類的正確性

​ 二、準備:爲類的靜態變量(也能夠稱爲類變量)分配內存,並將其初始化爲默認值(好比int 的默認值就是0)

​ 三、解析:將類中的符號引用轉換爲直接引用

類的初始化:爲類的靜態變量進行賦值(從代碼從上到下執行)

Java程序對類的使用方式可分爲兩種:

  • 主動使用

  • 被動使用

全部的Java虛擬機實現,在每一個類或接口被Java程序"首次主動使用"時才初始化他們,必定要記住,是首次而且仍是主動使用得時候纔會初始化類。

若是對其類或者接口主動使用致使初始化了(此時的初始化就說明加載、鏈接(鏈接的三個步驟,注意,此時的鏈接只完成類的靜態變量分配內存,並將其初始化爲默認值)已經完成了)

我這裏總結了7種主動使用:

——建立類的實例

——訪問某個類或接口的靜態變量,或者對該靜態變量賦值

——調用類的靜態方法

——反射(如class.forName())

——初始化一個類的子類

——Java虛擬機啓動時被代表爲啓動類的類

——JDK1.7開始提升的動態語言支持;

除了以上7種狀況,其餘使用Java類的方式都被看作是對類的被動使用,都不會致使類的初始化。

三、類的加載鏈接初始化詳細講解

​ 其實咱們知道類的加載的最終產品是位於內存中的Class對象,Class對象封裝了類在方法區內的數據結構,而且向Java程序員提供了訪問方法區內的數據結構的接口。

根據以上的總結,咱們知道類的鏈接其實就是當類被加載後,就進入鏈接階段。鏈接就是將已經讀入到內存的類的二進制數據合併到虛擬機的運行環境中去。那麼類的驗證的內容有哪些呢?

  • 類文件的結構檢查
  • 語義檢查
  • 字節碼驗證
  • 二進制兼容性的驗證

四、類加載器

​ 類的加載實際上是類加載器去完成的,咱們能夠把類加載器想象成一個小人,幫助JVM幹活的。那麼類加載器的定義是什麼呢,這裏按照我我的的理解總結了一下:

類加載器(classLoader):類加載器是用來把類加載到Java虛擬機的內存空間中(加載類的工具,類必定是由類加載器去加載)。從JDK1.2版本開始,類的加載過程採用雙親委託機制。這種機制能更好的保證Java平臺的安全。在此委託機制中,除了Java虛擬機自帶的根類加載器以外(由於根類加載器自己是沒有父加載器的),其他的類加載器都有且只有一個父加載器。當Java程序請求加載器loader1加載Sample類時,loader1首先委託本身的父加載器去加載Sample類,若父加載器能加載,則由父加載器完成加載任務,不然纔有加載器loader1自己加載Sample類。

類加載器分爲兩種類型:

一、Java虛擬機自帶的加載器

  • 根類加載器(BootstrapClassLoader),也稱啓動類加載器
  • 擴展類加載器(ExtensionClassLoader)
  • 系統(應用)類加載器(SystemClassLoader或者AppClassLoader)

二、用戶自定義的類加載器

  • java.lang.ClassLoader的子類(全部用戶自定義的類加載器都應該繼承抽象類ClassLoader類)
  • 用戶能夠定製類的加載方式

類加載器並不須要等到某個類被」首次主動使用「時再加載它

五、類加載器雙親委託機制詳解

​ 這一小節咱們來詳細瞭解一下類加載器的雙親委託機制。父親委託機制也稱爲雙親委託機制(我我的得理解實際上應該叫作父親委託機制,由於在源碼裏面是parent而不是parents):在父親委託機制中,各個加載器按照父子關係造成了熟悉結構(邏輯上的,好比下圖),除了啓動類加載器以外,其他的類加載器都有且只有一個父加載器。

如下幾種加載器從表面看是繼承關係,其實是包含關係哦

我舉例來看看父親委託機制的實際執行:

​ 對上圖執行流程我詳細得解釋一下類加載器父親委託機制具體是怎麼執行得:首先loader1和loader2是咱們自定義的加載器,loader1嘗試去加載Sample類,根據父親委託機制,其實並非由loader1去直接加載Sample類到虛擬機當中,相反,它是把這個加載任務轉交給系統類加載器去完成,系統類加載器再把這個加載任務轉交給擴展類加載器,而後擴展類加載器再轉交給根類加載器去完成,因爲根類加載器已是類加載器體系層次的最頂層,因此根類加載器會嘗試去Sample類到虛擬機當中(而後根類加載器不能加載,由於他是從特定的幾個目錄去加載),既然根類加載器沒法完成加載,他就會把這個任務返回給擴展類加載器(同理,原則上也不能加載),再讓系統類加載器去加載(通常是能夠加載成功)。最終再把這個流程返回給loader1,就宣告類加載過程結束。

六、獲取類加載器的幾種途徑

​ 既然咱們瞭解了類加載器的種類,那咱們也須要了解經過什麼方式能夠獲取到類加載器,獲取類加載器的方式我這裏總結了4種方式:

第一種:得到當前類的ClassLoader:

​ clazz.getClassLoder()

具體實現以下所示:

Class<?> clazz1 = Class.forName("java.lang.String");
System.out.println(clazz1.getClassLoader());複製代碼

第二種:得到當前線程上下文的ClassLoader:

​ Thread.currentThread().getContextClassLoader();

具體實現以下所示:

ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
System.out.println(contextClassLoader);複製代碼

第三種:得到系統ClassLoader:

​ ClassLoader.getSystemClassLoader();

第四種:得到調用者的ClassLoader

​ DriverManager.getCallerLoader()

​ 咱們還須要知道其實數組並非由類加載器加載建立的的,而是當被須要時,被jvm運行時自動建立的,對於數組來講,他的類加載器是和他元素的類型的類加載同樣的,若是元素類型是基本類型,則數組沒有類加載器

​ ClassLoader類自己默認是並行加載的的(parallel capable),若是子類想支持並行加載,是須要本身註冊的,用戶自定義加載器若須要並行加載,須要自行配置,經過調用registerAsParallelCapable()

七、總結

​ 經過以上得相關總結,咱們其實能夠發現,JVM學習並非像spring,springcloud都是應用框架,是能夠立刻作東西的,立竿見影,能夠立刻看到效果,JVM不是這樣的,涉及到了不少理論。不少同窗可能以爲不重要,感受學了也沒有,其實否則,就像練武同樣,只有你的內功修煉好了,再去練其餘的招式就會很容易,纔會精益求精,而JVM就至關於內功,因此可想而知,對於JVM的學習,顯然是很重要的。以上就是我對JVM類加載器相關總結,下一篇文章應該是推出關於結合java源碼理解類加載器得相關內容,固然後續也會推出JVM其餘板塊相關知識得相關總結。


最後,最近不少小夥伴找我要Linux學習路線圖,因而我根據本身的經驗,利用業餘時間熬夜肝了一個月,整理了一份電子書。不管你是面試仍是自我提高,相信都會對你有幫助!目錄以下:

免費送給你們,只求你們金指給我點個贊!

電子書 | Linux開發學習路線圖

也但願有小夥伴能加入我,把這份電子書作得更完美!

有收穫?但願老鐵們來個三連擊,給更多的人看到這篇文章

推薦閱讀:

相關文章
相關標籤/搜索