1. 程式人生 > >Tomcat啟動時與JVM關係及載入情況

Tomcat啟動時與JVM關係及載入情況

  說到本篇的tomcat類載入機制,不得不說翻譯學習tomcat的初衷。

  之前實習的時候學習javaMelody的原始碼,但是它是一個Maven的專案,與我們自己的web專案整合後無法直接斷點除錯。後來同事指導,說是直接把java類複製到src下就可以了。很納悶....為什麼會優先載入src下的java檔案(編譯出的class),而不是jar包中的class呢?

  現在瞭解tomcat的類載入機制,原來一切是這麼的簡單。

類載入

  在JVM中並不是一次性把所有的檔案都載入到,而是一步一步的,按照需要來載入。

  比如JVM啟動時,會通過不同的類載入器載入不同的類。當用戶在自己的程式碼中,需要某些額外的類時,再通過載入機制載入到JVM中,並且存放一段時間,便於頻繁使用。

  因此使用哪種類載入器、在什麼位置載入類都是JVM中重要的知識。

JVM類載入

  JVM類載入採用 父類委託機制,如下圖所示:

  JVM中包括集中類載入器:

  1 BootStrapClassLoader 引導類載入器

  2 ExtClassLoader 擴充套件類載入器

  3 AppClassLoader 應用類載入器

  4 CustomClassLoader 使用者自定義類載入器

  他們的區別上面也都有說明。需要注意的是,不同的類載入器載入的類是不同的,因此如果使用者載入器1載入的某個類,其他使用者並不能夠使用。

  當JVM執行過程中,使用者需要載入某些類時,會按照下面的步驟(父類委託機制):

  1 使用者自己的類載入器,把載入請求傳給父載入器,父載入器再傳給其父載入器,一直到載入器樹的頂層。

  2 最頂層的類載入器首先針對其特定的位置載入,如果載入不到就轉交給子類。

  3 如果一直到底層的類載入都沒有載入到,那麼就會丟擲異常ClassNotFoundException。

  因此,按照這個過程可以想到,如果同樣在CLASSPATH指定的目錄中和自己工作目錄中存放相同的class,會優先載入CLASSPATH目錄中的檔案。

Tomcat類載入

  在tomcat中類的載入稍有不同,如下圖:

  當tomcat啟動時,會建立幾種類載入器:

  1 Bootstrap 引導類載入器 

  載入JVM啟動所需的類,以及標準擴充套件類(位於jre/lib/ext下)

  2 System 系統類載入器 

  載入tomcat啟動的類,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位於CATALINA_HOME/bin下。

  3 Common 通用類載入器 

  載入tomcat使用以及應用通用的一些類,位於CATALINA_HOME/lib下,比如servlet-api.jar

  4 webapp 應用類載入器

  每個應用在部署後,都會建立一個唯一的類載入器。該類載入器會載入位於 WEB-INF/lib下的jar檔案中的class 和 WEB-INF/classes下的class檔案。

  當應用需要到某個類時,則會按照下面的順序進行類載入:

  1 使用bootstrap引導類載入器載入

  2 使用system系統類載入器載入

  3 使用應用類載入器在WEB-INF/classes中載入

  4 使用應用類載入器在WEB-INF/lib中載入

  5 使用common類載入器在CATALINA_HOME/lib中載入

問題擴充套件

  通過對上面tomcat類載入機制的理解,就不難明白 為什麼java檔案放在Eclipse中的src資料夾下會優先jar包中的class?

  這是因為Eclipse中的src資料夾中的檔案java以及webContent中的JSP都會在tomcat啟動時,被編譯成class檔案放在 WEB-INF/class 中。

  而Eclipse外部引用的jar包,則相當於放在 WEB-INF/lib 中。

  因此肯定是 java檔案或者JSP檔案編譯出的class優先載入。

  通過這樣,我們就可以簡單的把java檔案放置在src資料夾中,通過對該java檔案的修改以及除錯,便於學習擁有原始碼java檔案、卻沒有打包成xxx-source的jar包。

  另外呢,開發者也會因為粗心而犯下面的錯誤。

  在 CATALINA_HOME/lib 以及 WEB-INF/lib 中放置了 不同版本的jar包,此時就會導致某些情況下報載入不到類的錯誤。

  還有如果多個應用使用同一jar包檔案,當放置了多份,就可能導致 多個應用間 出現類載入不到的錯誤。