JVM中ClassLoader的學習

天天不是小可愛發表於2019-07-08

JVM中class loaderの學習


一、.class檔案和jvm的關係

類的載入

所有的編譯生成的.class檔案都會被直接載入到JVM裡面來嗎(並不

首先我們明確一個概念,.class檔案載入到jvm中意味著什麼——類的初始化

在虛擬機器規範中,我們規定,有且只有五種情況必須立即對類進行初始化

  • 建立類的例項(new一個物件)訪問某個類或者介面的靜態變數,對靜態變數賦值,呼叫類的靜態方法
  • reflection
  • 啟動類(main),直接使用Java.exe命令來執行某個主類(main方法)
  • 動態語言支援
  • 初始化主類,父類也會被初始化

.class的載入是消耗記憶體的,所以當然不能一次性的把所有的類載入再執行(本來就不快,這樣做不是更慢),程式的base class會完全載入到jvm裡面來,至於其他的類,它們都是在需要的時候再載入的,這樣是為了節省記憶體開銷。

怎麼載入

class是通過類的載入器裝在到jvm裡面來的,Java預設有三種類載入器

  • 引導類載入器(bootstrap class loader):它用來載入 Java 的核心庫,是用原生程式碼來實現的,並不繼承自 java.lang.ClassLoader
  • 擴充套件類載入器(extensions class loader):它用來載入 Java 的擴充套件庫。Java 虛擬機器的實現會提供一個擴充套件庫目錄。該類載入器在此目錄裡面查詢並載入 Java 類。
  • 系統類載入器(system class loader):它根據 Java 應用的類路徑(CLASSPATH)來載入 Java 類。一般來說,Java 應用的類都是由它來完成載入的。可以通過 ClassLoader.getSystemClassLoader()來獲取它。

img

雙親委派模型

如果一個類收到了載入的請求,它首先不會自己載入這個類,而是會把請求委託給父載入器去完成,依次向上。(如果自身完成不了再依次向下,直到丟擲ClassNotFound異常)

advantage:

防止記憶體中出現多份同樣的位元組碼(從安全性角度來說)

已經載入過的類,下次還會請求載入嗎?

class loader在成功載入某個類之後,會把得到的java.lang.Class類的例項快取起來。如果下次再碰見該類的載入請求,類載入器會直接使用快取的類的例項,不會再次載入

類載入器是什麼?

俄羅斯套娃

類載入器的Java類和所有其他的Java類一樣,都是要通過類載入器來載入的。

對於開發人員編寫的類載入器來說,父類是載入該類載入器的Java類的類載入器

類載入器樹狀組織結構示意圖

二、類的載入過程

載入器載入到jvm中,接下來其實又分了好幾個步驟

  • 載入,查詢並載入類的二進位制資料,在Java堆中也建立一個java.lang.Class類的物件
  • 連線,連線又包含三塊內容:驗證、準備、初始化。
    • 驗證,檔案格式、後設資料、位元組碼、符號引用驗證;
    • 準備,為類的靜態變數分配記憶體,並將其初始化為預設值;
    • 解析,把類中的符號引用轉換為直接引用
  • 初始化,為類的靜態變數賦予正確的初始值。

img

三、類載入器和web容器

對於執行在 Java EE™容器中的 Web 應用來說,類載入器的實現方式與一般的 Java 應用有所不同。不同的 Web 容器的實現方式也會有所不同。以 Apache Tomcat 來說,每個 Web 應用都有一個對應的類載入器例項。該類載入器也使用代理模式,所不同的是它是首先嚐試去載入某個類,如果找不到再代理給父類載入器。這與一般類載入器的順序是相反的。這是 Java Servlet 規範中的推薦做法,其目的是使得 Web 應用自己的類的優先順序高於 Web 容器提供的類。這種代理模式的一個例外是:Java 核心庫的類是不在查詢範圍之內的。這也是為了保證 Java 核心庫的型別安全

In a Java environment, class loaders are arranged in a parent-child tree. Normally, when a class loader is asked to load a particular class or resource, it delegates the request to a parent class loader first, and then looks in its own repositories only if the parent class loader(s) cannot find the requested class or resource. Note, that the model for web application class loaders differs slightly from this, as discussed below, but the main principles are the same.

When Tomcat is started, it creates a set of class loaders that are organized into the following parent-child relationships, where the parent class loader is above the child class loader:

      Bootstrap
          |
       System
          |
       Common
       /     \
  Webapp1   Webapp2 ...

Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:

Bootstrap classes of your JVM
/WEB-INF/classes of your web application
/WEB-INF/lib/
.jar of your web application
System class loader classes (described above)
Common class loader classes (described above)

If the web application class loader is configured with *

Bootstrap classes of your JVM
System class loader classes (described above)
Common class loader classes (described above)
/WEB-INF/classes of your web application
/WEB-INF/lib/
.jar of your web application

絕大多數情況下,Web 應用的開發人員不需要考慮與類載入器相關的細節。下面是幾條簡單的原則:

  • 每個 Web 應用自己的 Java 類檔案和使用的庫的 jar 包,分別放在 WEB-INF/classesWEB-INF/lib目錄下面。
  • 多個應用共享的 Java 類檔案和 jar 包,分別放在 Web 容器指定的由所有 Web 應用共享的目錄下面。
  • 當出現找不到類的錯誤時,檢查當前類的類載入器和當前執行緒的上下文類載入器是否正確。
Demo

跟著部落格做的一個ClassLoader小demo https://github.com/JhinQaQ/Classloader

參考資料

Java虛擬機器底層原理知識總結 https://github.com/doocs/jvm

tomcat7.0 ClassLoader http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html

深入探討Java類載入器 https://www.ibm.com/developerworks/cn/java/j-lo-classloader/

Java類載入器的使用場景有哪些?https://www.zhihu.com/question/46719811

相關文章