Java基礎-類加載器和核心機制

類加載機制
  • JVM把class文件加載到內存,並對數據進行校驗、解析和初始化,最終造成 JVM能夠直接使用的Java類型的過程。
    在這裏插入圖片描述
  • 將Java類的二進制代碼合併到JVM的運行狀態之中的過程
    • 驗證: 確保加載的類信息符合JVM規範,沒有安全方面的問題。
    • 準備: 正式爲類變量(static變量)分配內存並設置類變量初始值的階段,這些內存都將在方法區中進行分配
    • 解析: 虛擬機常量池內的符號引用替換爲直接引用的過程
  • 初始化:
    • 初始化階段是執行類構造器<clinit>()方法的過程。類構造器<clinit>()方法是由編譯器自動收集 類中的全部類變量的賦值動做和靜態語句塊(static塊)中的語句合併產生的。
    • 當初始化一個類的時候,若是發現其父類尚未進行過初始化、則須要先出發其父類的初始化
    • 虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確加鎖和同步。
類的主動引用(必定會發生類的初始化)
  • new一個類的對象
  • 調用類的靜態成員(除了final常量)和靜態方法
  • 使用java.lang.reflect包的方法對類進行反射調用
  • 當虛擬機啓動,java Hello,則必定會初始化Hello類。說白了就是先啓動main方法所在的類
  • 當初始化一個類,若是其父類沒有被初始化,則先會初始化他的父類
類的被動引用(不會發生類的初始化)
  • 當訪問一個靜態域時,只有真正聲明這個域的類纔會被初始化
  • 經過子類引用父類的靜態變量,不會致使子類初始化 – 經過數組定義類引用,不會觸發此類的初始化
  • 引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中了)
類加載器的做用

將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法 區中的運行時數據結構,在堆中生成一個表明這個類的java.lang.Class 對象,做爲方法區類數據的訪問入口。java

類緩存

標準的Java SE類加載器能夠按要求查找類,但一旦某個類被加載到類加載 器中,它將維持加載(緩存)一段時間。不過,JVM垃圾收集器能夠回收 這些Class對象。mysql

ClassLoder

做用
  • java.lang.ClassLoader類的基本職責就是根據一個指定的類的名稱, 找到或者生成其對應的字節代碼,而後從這些字節代碼中定義出一個 Java 類,即 java.lang.Class類的一個實例。
  • 除此以外,ClassLoader還負責加載 Java 應用所需的資源,如圖像文 件和配置文件等。
  • 相關方法
    • getParent() 返回該類加載器的父類加載器。
    • loadClass(String name) 加載名稱爲 name的類,返回的結果是 java.lang.Class類的實例。
    • findClass(String name) 查找名稱爲 name的類,返回的結果是 java.lang.Class類的實例。
    • findLoadedClass(String name) 查找名稱爲 name的已經被加載過的類,返回的結果是 java.lang.Class類的實例
    • defineClass(String name, byte[] b, int off, int len) 把字節數組 b中的內容轉換成 Java 類,返回的結果是 java.lang.Class類的實例.這個方法被聲明爲 final的。
    • resolveClass(Class<?> c) 連接指定的 Java 類。

對於以上給出的方法,表示類名稱的 name參數的值是類的二進制名稱。須要注意的是內部類的表示,如com.example.Sample$1com.example.Sample$Inner等表示方式。web

類加載器的層次結構
  • 引導類加載器(bootstrap class loader)
    • 它用來加載 Java 的核心庫(JAVA_HOME/jre/lib/rt.jar,sun.boot.class.path路徑下的 內容),是用原生代碼來實現的,並不繼承自 java.lang.ClassLoader。
    • 加載擴展類和應用程序類加載器。並指定他們的父類加載器。
  • 擴展類加載器(extensions class loader)
    • 用來加載 Java 的擴展庫(JAVA_HOME/jre/ext/*.jar,java.ext.dirs路徑下的內容) 。 Java 虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄裏面查找並加載 Java 類。
    • sun.misc.Launcher$ExtClassLoader實現
  • 應用程序類加載器(application class loader)
    • 它根據 Java 應用的類路徑(classpath, java.class.path 路徑下的內容)來加載 Java 類。 通常來講,Java 應用的類都是由它來完成加載的。
    • sun.misc.Launcher$AppClassLoader實現
  • 自定義類加載器
    • 開發人員能夠經過繼承 java.lang.ClassLoader類的方式
    • 實現本身的類加載器,以知足一些特殊的需求。

類加載器的代理模式

代理模式
  • 交給其餘加載器來加載指定的類
雙親委託機制
  • 就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委 託給父類加載器,依次追溯,直到最高的爺爺輩的,若是父類加載器 能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載 任務時,才本身去加載。
  • 雙親委託機制是爲了保證 Java 核心庫的類型安全。
  • 這種機制就保證不會出現用戶本身能定義java.lang.Object類的狀況。
  • 類加載器除了用於加載類,也是安全的最基本的屏障。
雙親委託機制是代理模式的一種
  • 並非全部的類加載器都採用雙親委託機制。
  • tomcat服務器類加載器也使用代理模式,所不一樣的是它是首先嚐試去加載某個類,若是找不到再代理給父類加載器。 這與通常類加載器的順序是相反的
自定義類加載器的流程
  • 一、首先檢查請求的類型是否已經被這個類裝載器裝載到命名空間中了,若是已經裝載,直接返回;不然轉入步驟2
  • 二、委派類加載請求給父類加載器(更準確的說應該是雙親類加載器,真個虛擬機中各類類加載器最終會呈現樹狀結構),若是父類加載器可以完成,則返回父類加載器加載的Class實例;不然轉入步驟3
  • 三、調用本類加載器的findClass(…)方法,試圖獲取對應的字節碼,若是獲取的到,則調用defineClass(…)導入類型到方法區;若是獲取不到對應的字節碼或者其餘緣由失敗,返回異常給loadClass(…)loadClass(…)轉拋異常,終止加載過程(注意:這裏的 異常種類不止一種)。

注意:被兩個類加載器加載的同一個類,JVM不認爲是相同的類。sql

線程上下文類加載器
  • 雙親委託機制以及默認類加載器的問題bootstrap

    • 通常狀況下, 保證同一個類中所關聯的其餘類都是由當前類的類加載器所加載的.。 好比,ClassA自己在Ext下找到,那麼他裏面new出來的一些類也就只能用Ext去查找了(不會低一個級別),因此有些明明App能夠找到的,卻找不到了。
    • JDBC API,他有實現的driven部分(mysql/sql server),咱們的JDBC API都是由Boot或者Ext來載入的,可是 JDBC driver倒是由Ext或者App來載入,那麼就有可能找不到driver了。在Java領域中,其實只要分紅這種Api+SPI( Service Provide Interface,特定廠商提供)的,都會遇到此問題。
    • 常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的接口由 Java 核心庫來提供,如 JAXP 的 SPI 接口定 義包含在 javax.xml.parsers 包中。SPI 的接口是 Java 核心庫的一部分,是由引導類加載器來加載的;SPI 實現的 Java 類通常是由系統類加載器來加載的。引導類加載器是沒法找到 SPI 的實現類的,由於它只加載 Java 的核心庫
  • 一般當你須要動態加載資源的時候 , 你至少有三個 ClassLoader 能夠選擇 :數組

    • 1.系統類加載器或叫做應用類加載器 (system classloader or application classloader)
    • 2.當前類加載器
    • 3.當前線程類加載器
  • 當前線程類加載器是爲了拋棄雙親委派加載鏈模式。緩存

    • 每一個線程都有一個關聯的上下文類加載器。若是你使用new Thread()方式生成新的線程,新線程將繼承其父線程的上下文類加載器。若是程序對線程上下文類加載器沒有任何改動的話,程序中全部的線程將都使用系統類加載器做爲上下文類加載器。
  • Thread.currentThread().getContextClassLoader()tomcat

TOMCAT服務器的類加載機制
  • TOMCAT不能使用系統默認的類加載器。
    • 若是TOMCAT跑你的WEB項目使用系統的類加載器那是至關危險的,你能夠直接是無忌憚是操做系統的各個目錄了。
    • 對於運行在 Java E容器中的 Web 應用來講,類加載器的實現方式與一 般的 Java 應用有所不一樣。
    • 每一個 Web 應用都有一個對應的類加載器實例。該類加載器也使用代理模 式(不一樣於前面說的雙親委託機制),所不一樣的是它是首先嚐試去加載某個類,若是找不到再代理給父類加載器。這與通常類加載器的順序是相反的 。但也是爲了保證安全,這樣核心庫就不在查詢範圍以內。
OSGI原理介紹
  • OSGi™是 Java 上的動態模塊系統。它爲開發人員提供了面向服務和基於組件的運 行環境,並提供標準的方式用來管理軟件的生命週期。
  • OSGi 已經被實現和部署在不少產品上,在開源社區也獲得了普遍的支持。Eclipse 就是基於 OSGi 技術來構建的。
  • 原理:
    • OSGi 中的每一個模塊(bundle)都包含 Java 包和類。模塊能夠聲明它所依賴的須要導入 (import)的其它模塊的 Java 包和類(經過 Import-Package),也能夠聲明導出( export)本身的包和類,供其它模塊使用(經過 Export-Package)。也就是說須要可以隱藏和共享一個模塊中的某些 Java 包和類。這是經過 OSGi 特有的類加載器機制來實現的。OSGi 中的每一個模塊都有對應的一個類加載器。它負責加載模塊本身包含的 Java 包和類。當它須要加載 Java 核心庫的類時(以 java開頭的包和類),它會代理給父類加載器(一般是啓動類加載器)來完成。當它須要加載所導入的 Java 類時,它會 代理給導出此 Java 類的模塊來完成加載。模塊也能夠顯式的聲明某些 Java 包和類,必 須由父類加載器來加載。只須要設置系統屬性org.osgi.framework.bootdelegation 的值便可。

本文同步分享在 博客「cwl_java」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。安全

相關文章
相關標籤/搜索