在使用java命令運行主類(main)的時候,首先要經過類加載器將類加載到JVM內存中去。主類在運行過程當中若是用到其餘的類就會逐步加載這些類。jar包裏的類並非一次性加載的,是使用的時候才加載的。java
類加載過程分爲如下幾步:web
加載 》驗證 》準備 》解析 》初始化 》使用 》卸載bootstrap
一、加載:在硬盤上經過IO讀入字節碼文件,使用到類的時候纔會加載,例如調用main()方法,new對象等等。數組
二、驗證:校驗字節碼文件的正確性tomcat
三、準備:給類的靜態變量分配內存,而且賦予默認值安全
四、解析:將符號引用替換爲直接引用,該階段會把一些靜態方法替換爲指向數據所存內存的地址指針或句柄,即靜態連接過程(類加載期間完成)。動態連接是在程序運行期間完成的,將符號引用替換爲直接引用。服務器
五、初始化:對類的靜態變量初始化爲指定值,執行靜態代碼塊。app
雙親委派機制就是在加載某個類的時候會先委託父類加載器(爸爸)加載,父類加載器就會繼續委託其父類加載器(爺爺輩)加載,若是全部父類加載器都沒有找到目標類,則在本身的加載類路徑中查找目標類進行加載。簡言之就是有事情作的時候,先交給爸爸作,爸爸作不了就交給爺爺作,爺爺也作不了就只能本身作了。也就是拼爹機制。jvm
假設你本身寫了一個類Test.當在加載Test的時候,應用程序類加載器會委託擴展類加載器加載,擴展類加載器會委託啓動類加載器加載,啓動類加載器在加載路徑中沒法找到Test類,就退回給擴展類加載器,擴展類加載器收到回覆,就在本身的加載類路徑中查找該類,找不到就退回給應用程序加載器加載。應用程序類在加載類路徑中查找要加載的類進行加載。jsp
爲何要設置雙親委派機制呢:
一、避免重複加載
二、沙箱安全機制,避免核心API庫被篡改。(好比你本身寫了一個String類,是不會加載的)
要實現自定義加載器只須要繼承ClassLoader類,該類有兩個核心方法,
public class MyClassLoaderTest { static class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } private byte[] loadByte(String name) throws Exception { name = name.replaceAll("\\.", "/"); FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } protected Class<?> findClass(String name) throws ClassNotFoundException{ try { byte[] data = loadByte(name); //defineClass將一個字節數組轉爲Class對象,這個字節數組是class文件讀取後最終 的字節數組。 return defineClass(name, data, 0, data.length); } catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); } } } public static void main(String args[]) throws Exception { MyClassLoader classLoader = new MyClassLoader("D:/test"); Class clazz = classLoader.loadClass("com.tuling.jvm.User1"); Object obj = clazz.newInstance(); Method method= clazz.getDeclaredMethod("sout", null);38 method.invoke(obj, null); System.out.println(clazz.getClassLoader().getClass().getName()); } } 運行結果: =======本身的加載器加載類調用方法======= com.tuling.jvm.MyClassLoaderTest$MyClassLoader
在如下幾種狀況下須要打破雙親委派機制:
一、同一個容器裏面部署多個應用,這幾個應用都依賴於同一個第三方類庫的不一樣版本
二、多個應用共享同一個版本的類庫。
三、容器依賴的類庫與應用程序的類庫分開
Tomcat是個web容器, 那麼它要解決什麼問題:
1. 一個web容器可能須要部署兩個應用程序,不一樣的應用程序可能會依賴同一個第三方類庫的不一樣版本,不能要求同一個類庫在同一個服務器只有一份,所以要保證每一個應用程序的類庫都是獨立的,保證相互隔離。
2. 部署在同一個web容器中相同的類庫相同的版本能夠共享。不然,若是服務器有10個應用程序,那麼要有10份相同的類庫加載進虛擬機,這是扯淡的。
3. web容器也有本身依賴的類庫,不能於應用程序的類庫混淆。基於安全考慮,應該讓容器的類庫和程序的類庫隔離開來。
4. web容器要支持jsp的修改,咱們知道,jsp 文件最終也是要編譯成class文件才能在虛擬機中運行,但程序運行後修改jsp已是司空見慣的事情,不然要你何用? 因此,web容器須要支持 jsp 修改後不用重啓。
Tomcat 若是使用默認的類加載機制行不行?
答案是不行的。爲何?
一、若是使用默認的類加載器機制,那麼是沒法加載兩個相同類庫的不一樣版本的,默認的累加器是無論你是什麼版本的,只在意你的全限定類名,而且只有一份。
二、默認的類加載器是可以實現的,由於他的職責就是保證惟一性。
三、同1。
四、咱們想咱們要怎麼實現jsp文件的熱修改,jsp 文件其實也就是class文件,那麼若是修改了,但類名仍是同樣,類加載器會直接取方法區中已經存在的,修改後的jsp是不會從新加載的。那麼怎麼辦呢?咱們能夠直接卸載掉這jsp文件的類加載器,因此你應該想到了,每一個jsp文件對應一個惟一的類加載器,當一個jsp文件修改了,就直接卸載這個jsp類加載器。從新建立類加載器,從新加載jsp文件。
CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader則是Tomcat本身定義的類加載器,它們分別加載/common/*
、/server/*
、/shared/*
(在tomcat 6以後已經合併到根目錄下的lib目錄下)和/WebApp/WEB-INF/*
中的Java類庫。其中WebApp類加載器和Jsp類加載器一般會存在多個實例,每個Web應用程序對應一個WebApp類加載器,每個JSP文件對應一個Jsp類加載器。
從圖中的委派關係中能夠看出: