摘要:標準擴展類加載器,它負責加載或由系統變量指定位置中的類庫加載到內存中。系統類加載器,它負責將類路徑中的類庫加載到內存。
類加載機制大家應該已經非常熟悉了,采取雙親委派機制,當加載一個類時,首先將加載任務委托給父類加載器,依次遞歸,如果父類加載器可以完成加載任務,就成功返回;如果父類無法加載,才由自己加載。
雙親委派機制的作用:防止內存中出現多份相同的字節碼。
其他規則:
1.隱式加載:在當前類中所有new的對象,如果沒有被加載,則使用當前類的類加載器加載 如果類A中引用了類B,Java虛擬機將使用加載類A的類加載器去加載類B
2.不同類加載器加載的類是不同的,通過類加載器+類全路徑來唯一標識一個類
JVM預定義的三種類加載器:
1.Bootstrap ClassLoader:啟動類加載器,它負責將JAVA_HOME/lib下面的類庫加載到內存中,如rt.jar;啟動類加載器是由C++寫的二進制代碼,不是java類,在JVM啟動的時候Bootstrap就已經啟動。
2.Extension ClassLoader:標準擴展類加載器,它負責加載JAVA_HOME/lib/ext或由系統變量java.ext.dir指定位置中的類庫加載到內存中。
3.APP ClassLoader:系統類加載器(System ClassLoader),它負責將類路徑CLASSPATH中的類庫加載到內存。
加載順序圖如下:
圖中的BootStrapClassLoader、ExtClassLoader、APPClassLoader不是真正的繼承關系,只是邏輯上的上下級類加載器;
實際上的類關系如下圖:
可以看到ExtClassLoader和APPCLassLoader都繼承自URLClassLoader,也就證實了二者并非真正的繼承關系;
通過上圖可以看到最頂層的類是抽象類ClassLoader:是所有類加載器的基類(除了啟動類加載器),定義了類加載最核心的操作;
SecureClassLoader:添加了關聯類源碼、關聯系統權限支持
URLClassLoader:支持從jar文件和文件夾中獲取class
ExtClassLoader:擴展類加載器Extension ClassLoader
APPClassLoader:系統類加載器,也稱為System ClassLoader
ClassLoader:
父子類加載器是通過ClassLoader一個parent屬性來標識,APPClassLoader的父加載器是ExtClassLoader,ExtClassLoader的父加載器是null。
ClassLoader提供了兩個構造器,一個是沒有參數的,一個是有參數的;如下圖:
沒有參數的構造器默認將系統類加載器作為parent加載器;
有參數的構造器將參數指定的加載器作為父類加載器;
Launcher:
ExtClassLoader和AppClassLoader都是Launcher的子類,在ClassLoader初始化或者直接通過ClassLoader的getSystemClassLoader()獲取的時候會調用initSystemClassLoader(),從而調用sun.misc.Launcher.getLauncher(),將系統類加載器賦值給ClassLoader的scl變量;
我們看下Launcher類初始化的時候都做了什么工作,如圖:
主要是三部工作:
1.創建ExtClassLoader
2.創建AppClassLoader
3.將線程系統類加載器設置為線程上下文類加載器,什么是上線文類加載器?
線程上下文類加載器:
java提供了為很多服務商提供了接口,簡稱SPI(Service Provider Interface),具體的實現由各廠商提供,例如mysql驅動,oracle驅動等。例如:mysql驅動加載接口類在rt.jar中,由啟動類加載器加載,具體實現類在mysql驅動包中,驅動包一般放到我們自己的程序路徑lib下,應該由系統類加載器加載;但是在使用如下代碼進行數據庫連接使用操作的時候,就會出現在rt.jar中要加載驅動包里代碼的情況(類加載器是啟動類加載器),由隱式加載規則可知,驅動包也要使用啟動類加載器加載,由類加載機制可知,是無法通過啟動類加載器來加載的;那這種情況怎么辦呢,就要通過線程上下文類加載器來解決。
上面描述的情況如下:使用jdbc進行數據庫操作如下
1.Class.forName("com.mysql.jdbc.Driver");// 加載mysql驅動 2.Connection conn = DriverManager.getConnection(url);//創建連接 3.Statement stmt = conn.createStatement();//得到statement對象 4.操作數據庫 關閉連接。。
第一步在實例化Driver時,會調用DriverManager的registerDriver()方法收集divers,將驅動類注冊到DriverManager容器中,DriverManage的drivers容器:
注冊的代碼:
Class.forName("com.mysql.jdbc.Driver")相當于:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class driversClass = loader.loadClass("com.mysql.jdbc.Driver");
driversClass.newInstance();
由此可見com.mysql.jdbc.Driver是由我們自己應用類加載器AppClassLoader進行加載;
第二步通過DriverManager.getConnection(url),會循環獲取drivers中的driver,調用具體driver實現里的cnnect()方法,進行連接
caller.getClassLoader()是啟動類加載器為null,因此callerCL為系統類加載器
getConnection通過isDriverAllowed方法校驗類是否有權限被加載
通過AppClassLoader來加載Driver看是否和已注冊的Driver是同一個類,如果是則調用driver的connect方法
在java6以后,引入了service provider概念,在/META-INF/services/java.sql.Driver文件中配置需要加載的驅動類,
在DriverManager初始化的時候會調用loadInitialDrivers方法,
會使用AppClassLoader進行加載,所以在自己程序中可以不用Class.forName顯示調用。
上面包類結構如下圖:
tomcat類加載
我們運行tomcat的多個實例,不想安裝tomcat軟件副本,我們可以配置多個工作目錄,每個運行實例獨占一個工作目錄,但是共享一個安裝目錄。
變量解釋:
CATALINA_HOME:tomcat安裝目錄,多個工作目錄可共享安裝目錄
CATALINA_BASE:tomcat工作目錄,tomcat每個運行實例需要使用自己的conf、logs、temp、webapps、work、shared目錄,CATALINA_BASE就是指向這個目錄
如下圖:兩個應用公用CATALINA_HOME,CATALINA_BASE指向各自工作目錄
首先看下tomcat在啟動時類初始化類加載器過程
首先是創建commonClassLoader,commonClassLoader加載的是配置文件catalina.properties中配置的
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=
例如在我們的服務器上路徑是:
/opt/soft/tomcat/lib、/opt/soft/tomcat/lib/*.jar
因為server.loader和shared.loader未配置具體加載目錄信息,catalinaLoader和sharedLoader默認為commonLoader(在tomcat5以后catalinaLoader和sharedLoader默認不啟用)
tomcat一共定義了兩種類加載器
StandardClassLoader:實例化commonloader、catalinaLoader、sharedLoader,不提供熱部署功能,遵循雙親委派機制
WebappClassLoader:和context級容器相關聯,加載web程序,支持其加載路徑下資源改變后重新加載,不遵循雙親委派機制。
其類繼承關系如下:
除此之外還有兩個類:WebappLoader和VirtualWebappLoader,該兩個類不是類加載器,只是對WebappClassLoader做了封裝,對熱部署、生命周期控制等功能做了一些控制;
VirtualWebappLoader是WebappLoader的子類,主要是和conf/context.xml這個文件相關聯,主要功能是加載context.xml配置文件中設置的一些java類庫,由于WebappClassLoader只能加載WEB-INF/class和WEB-INF/lib下的類庫。而想擴展一下加載路徑又不想添加到WEB-INF/lib中的時候,可以配置在context.xml文件中。
自己實現類記載器只要實現findclass即可,這里為了實現特殊目的而override了loadClass();WebappClassLoader重寫了loadClass方法,先自己加載,如果加載不了再進行其他操作。
所以在Tomcat 6中默認情況下,不是完全按照先Tomcat的lib再Web應用的lib這種順序去加載類。
Jar包的加載順序是:
1)JRE中的Java基礎包
2)Web應用WEB-INF/lib下的包
3)Tomcat/lib下的包
如果想要在Web應用間共享一些Jar包,則不僅需要將公共包放在Tomcat的lib下,還要刪掉Web應用lib中的包,否則Tomcat啟動時還是會優先加載Web應用lib下的包的。
如果想要自己指定一個Tomcatlib和Web應用lib之外的ClassPath,除了修改Tomcat啟動腳本外,可以為不同Web應用的Context指定一個VirtualWebappLoader,但源碼注釋中寫到不推薦在生產環境中使用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70300.html
摘要:程序計數器程序計數器是一塊較小的內存空間,它的作用可以看做是當前線程所執行的字節碼的行號指示器。它的主要缺點有兩個一個是效率問題,標記和清除過程的效率都不 Jvm 相關 類加載機制 本段參考 http://www.importnew.com/2374... 類加載概念 類加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然后在堆區創建一個ja...
摘要:驗證過程驗證過程的目的是為了確保文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。二虛擬機字節碼執行引擎虛擬機的執行引擎自行實現,可以自行制定指令集與執行引擎的結構體系。 本篇博客主要針對Java虛擬機的類加載機制,虛擬機字節碼執行引擎,早期編譯優化進行總結,其余部分總結請點擊Java虛擬總結上篇 。 一.虛擬機類加載機制 概述 虛擬機把描述類的數據從Clas...
摘要:典型應用鎖和同步器框架的核心類,就是通過調用和實現線程的阻塞和喚醒的,而的方法實際是調用的方式來實現。 前言 Unsafe是位于sun.misc包下的一個類,主要提供一些用于執行低級別、不安全操作的方法,如直接訪問系統內存資源、自主管理內存資源等,這些方法在提升Java運行效率、增強Java語言底層資源操作能力方面起到了很大的作用。但由于Unsafe類使Java語言擁有了類似C語言指針...
摘要:二驗證驗證主要是為了確保文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機的自身安全。五初始化類的初始化階段是類加載過程的最后一步,該階段才真正開始執行類中定義的程序代碼或者說是字節碼。 關注我,每天三分鐘,帶你輕松掌握一個Java相關知識點。 虛擬機(JVM)經常出現在我們面試中,但是工作中卻很少遇到,導致很多同學沒有去了解過。其實除了應付面試,作為java程序員,了解...
閱讀 2096·2021-11-23 09:51
閱讀 2839·2021-11-22 15:35
閱讀 2937·2019-08-30 15:53
閱讀 1037·2019-08-30 14:04
閱讀 3276·2019-08-29 12:39
閱讀 1802·2019-08-28 17:57
閱讀 1086·2019-08-26 13:39
閱讀 551·2019-08-26 13:34