在JVM之下–類加載器

在許多開發人員中,類加載器是Java語言的底層,并且經常被忽略。 在ZeroTurnaround上 ,我們的開發人員必須生活,呼吸,飲食,喝酒,并且幾乎與類加載器保持親密關系,才能生產JRebel技術,該技術在類加載器級別進行交互以提供實時運行時類重裝,從而避免了冗長的重建/重新包裝/重新部署周期。
以下是我們從類加載器中學到的一些知識,其中包括一些調試技巧,這些技巧將有望為您節省時間和將來的總服務臺。

一個類加載器只是一個普通的java對象

是的,這并不聰明,除了JVM中的系統類加載器之外,類加載器只是一個Java對象! 這是一個抽象類ClassLoader,可以由您創建的類實現。 這是API:

public abstract class ClassLoader {public Class loadClass(String name);protected Class defineClass(byte[] b);public URL getResource(String name);public Enumeration getResources(String name);public ClassLoader getParent()}

看起來很簡單,對吧? 讓我們逐個方法看一下。 中心方法是loadClass,它僅使用String類名,然后返回實際的Class對象。 如果您以前使用過類加載器,則此方法可能是最熟悉的方法,因為它是日常編碼中使用最多的方法。 defineClass是JVM中的最終方法,該方法從網絡上的文件或位置獲取字節數組,并產生相同的結果(即Class對象)。

類加載器還可以從類路徑中找到資源。 它的工作方式與loadClass方法類似。 有兩種方法,getResource和getResources,它們返回一個URL或URL的枚舉,這些URL或URL的枚舉指向資源,該資源表示傳遞給方法的名稱。

每個類加載器都有一個父級。 getParent返回與Java繼承無關的classloader父類,而是一個鏈表樣式的連接。 稍后我們將對此進行更深入的研究。

類加載器是惰性的,因此僅在運行時請求類時才加載類。 類是由調用該類的資源加載的,因此,在運行時,一個類可以由多個類加載器加載,具體取決于從何處引用它們,以及哪個類加載器加載了引用了這些類的類…哎呀,我cross地了! 讓我們看一些代碼。

public class A {public void doSmth() {B b = new B();b.doSmthElse();}}

在這里,我們有一個類A在其方法范圍內調用類B的構造函數。 在幕后這是正在發生的事情

A.class.getClassLoader().loadClass(“B”);

最初加載類A的類加載器被調用以加載類B。

類加載器是分層的,但是像孩子一樣,他們并不總是問父母

每個類加載器都有一個父類加載器。 當一個類加載器被要求提供一個類時,它通常會直接轉到父類加載器,首先調用loadClass,而后者又會詢問它的父類,依此類推。 如果要求具有相同父級的兩個類加載器加載同一類,則父級將只執行一次。 當兩個類加載器分別加載同一個類時,這將非常麻煩,因為這可能會導致問題,我們將在后面討論。

當設計JEE規范時,Web類加載器被設計為以相反的方式工作-很棒。 讓我們看一下下圖作為示例。


模塊WAR1有自己的類加載器,并且更喜歡自行加載類,而不是委托給其父級(由App1.ear定義的類加載器)。 這意味著不同的WAR模塊(例如WAR1和WAR2)無法看到彼此的類。 App1.ear模塊具有自己的類加載器,并且是WAR1和WAR2類加載器的父級。 當WAR1和WAR2類加載器需要在層次結構中委派請求時,即WAR類加載器范圍之外需要一個類時,它們將使用App1.ear類加載器。 實際上,WAR類會覆蓋同時存在的EAR類。 最后,EAR類加載器的父級是容器類加載器。 EAR類加載器會將請求委派給容器類加載器,但它的執行方式與WAR類加載器不同,因為EAR類加載器實際上更喜歡委托而不是本地類。 如您所見,這變得非常繁瑣,并且與普通的JSE類加載行為不同。

平面類路徑

我們討論了系統類加載器如何通過類路徑查找已請求的類。 該類路徑可能包含目錄或JAR文件,查找它們的順序實際上取決于您使用的JVM。 您在類路徑上可能需要該類的多個副本或版本,但是您將始終在類路徑上找到該類的第一個實例。 本質上,這只是資源列表,這就是為什么將其稱為扁平資源。 結果,在查找資源時,遍歷類路徑列表通常會比較慢。

當使用相同類路徑的應用程序想要使用類的不同版本時,可能會發生問題,讓我們以Hibernate為例。 當類路徑上存在兩個版本的Hibernate JAR時,一個版本不能比一個應用程序的版本路徑在另一個應用程序的類路徑上更高,這意味著兩個版本都必須使用相同的版本。 解決此問題的一種方法是使用所有必需的庫使應用程序(WAR)膨脹,以便它們使用其本地資源,但這會導致難以維護的大型應用程序。 歡迎來到JAR地獄! OSGi在此提供了一種解決方案,因為它允許對JAR文件或捆綁軟件進行版本控制,從而形成一種機制,允許連接到特定版本的JAR文件,從而避免了平坦的類路徑問題。

如何調試類加載錯誤?

NoClassDefFoundError / ClassNotFoundException / ClassNoDefFoundException?

因此,您遇到了上述錯誤/異常。 好吧,這個班級真的存在嗎? 不要在IDE中尋找麻煩,因為在那兒編譯類是必須的,因為它必須在那里,否則您將獲得編譯時異常。 這是一個運行時異常,因此在運行時我們要查找它說我們缺少的類……但是您從哪里開始呢? 考慮下面的代碼…

Arrays.toString((((URLClassLoader) Test.class.getClassLoader()).getURLs()));

此代碼返回Test正在使用的類加載器的類路徑上所有jar和目錄的數組列表。 現在,我們可以看到神秘類應該存在的JAR或位置實際上在類路徑上。 如果不存在,請添加! 如果確實存在,請檢查JAR /目錄,以確保您的類確實存在于該位置,并在缺少該類時添加它。 這是導致此錯誤情況的兩個典型問題。

NoSuchMethodError / NoSuchFieldError / AbstractMethodError / IllegalAccessError嗎?

現在變得越來越有趣了! 這些都是IncompatibleClassChangeError的所有子類。 我們知道類加載器已經找到了想要的類(按名稱),但是顯然它沒有找到正確的版本。
在這里,我們有一個稱為Test的類,它正在調用另一個類Util,但是BANG –我們遇到了異常! 讓我們看一下要調試的下一個代碼片段:

Test.class.getClassLoader().getResource(Util.class.getName().replace('.', '/') + ".class");

我們在類Test的類加載器上調用getResource。 這將向我們返回Util資源的URL。 請注意,我們已替換了“。” 帶有“ /”,并在字符串末尾添加“ .class”。 這會將我們正在尋找的類的包和類名(從類加載器的角度來看)更改為文件系統上的目錄結構和文件名-簡潔。 這將向我們顯示我們已加載的確切類,并且可以確保它是正確的版本。 我們可以在命令提示符下在類上使用javap -private來查看字節碼并檢查實際存在的方法和字段。 您可以輕松地查看該類的結構,并驗證是您還是瘋了的Java運行時! 相信我,在一個或另一個階段,您都會同時問這兩個問題,幾乎每次都是您!

LinkageError / ClassCastException / IllegalAccessError

如果兩個不同的類加載器加載同一個類,并且它們嘗試進行交互,則可能會發生這種情況。 是的,現在有點毛了。 這可能會導致問題,因為我們不知道它們是否將從同一位置加載類。 怎么會這樣 讓我們看下面的代碼,它們仍然在Test類中:

Factory.instance().sayHello();

該代碼看起來非常干凈和安全,尚不清楚如何從此行出現錯誤。 我們正在調用靜態工廠方法來獲取Test類的實例,并在其上調用方法。 讓我們看一下該支持圖像,以顯示引發異常的原因。


在這里,我們可以看到一個Web類加載器(加載了Test類)將優先使用本地類,因此,當它引用一個類時,將盡可能由Web類加載器加載。 到目前為止還算簡單。 Test類使用Factory類來獲取Util類的實例,這在Java中是很典型的做法,但是Factory類在WAR中并不存在,因為它是一個外部庫。 這是沒有問題的,因為Web類加載器可以委托給共享類加載器,后者可以看到Factory類。 請注意,共享類加載器現在正在加載它自己的Util類版本,因為當Factory實例化該類時,它使用了共享類加載器(如前面的第一個示例所示)。 Factory類將Util對象(由共享類加載器創建)返回給WAR,WAR然后嘗試使用該類,并將該類有效地強制轉換為同一類的潛在不同版本(Web類加載器可見的Util類) )。 繁榮!

我們可以在兩個地方(Factory.instance()方法和Test類)中運行與以前相同的代碼,以查看每個Util類從何處加載。

Test.class.getClassLoader().getResource(Util.class.getName().replace('.', '/') + ".class"));

希望這可以使您對類加載的世界有所了解,而不是不了解類加載器,現在可以帶著恐懼和不確定性來欣賞它! 感謝您的閱讀并將其制作到最后。 我們都希望您從ZeroTurnaround祝您圣誕快樂,新年快樂! 編碼愉快!

參考: 在JVM的底層– Java出現日歷博客中來自JCG合作伙伴 Simon Maple的類加載器 。

翻譯自: https://www.javacodegeeks.com/2012/12/under-the-jvm-hood-classloaders.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/370059.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/370059.shtml
英文地址,請注明出處:http://en.pswp.cn/news/370059.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

matplotlib繪制餅狀圖

源自http://blog.csdn.net/skyli114/article/details/77508430?ticketST-41707-PzNbUDGt6R5KYl3TkWDg-passport.csdn.net pyplot使用plt.pie()來繪制餅圖 1 import matplotlib.pyplot as plt 2 labels frogs, hogs, dogs, logs 3 sizes 15, 20, 45, 10 # [15,20,45,10…

自適應寬度元素單行文本省略用法探究

單行文本省略是現代網頁設計中非常常用的技術,幾乎每個站點都會用到。單行文本省略適用于顯示摘要信息的場景,如列表標題、文章摘要等。在響應式開發中,自適應寬度元素單行文本省略容易失效不起作用,對網頁開發這造成困擾。因此&a…

P3390 【模板】矩陣快速冪

題目背景 矩陣快速冪 題目描述 給定n*n的矩陣A,求A^k 輸入輸出格式 輸入格式: 第一行,n,k 第2至n1行,每行n個數,第i1行第j個數表示矩陣第i行第j列的元素 輸出格式: 輸出A^k 共n行,每行n個數&…

c#精彩編程200例百度云_永安市教育局被授予“人工智能編程教育試驗區”

11月28日,“第二屆人工智能與機器人教育大會青少年人工智能與編程教育主題論壇”在廈門召開。永安市教育局被中國教育發展戰略學會人工智能與機器人教育專委會授予“人工智能編程教育試驗區”牌匾,巴溪灣小學、西門小學、三中、一中附屬學校、實驗小學等…

python中+=和=+的區別

本文原創,版權屬作者個人所有,如需轉載請聯系作者本人。Q&微:155122733 -------------------------------------------------------------------------------------------------------- a1 代表在原值上更改 aa1相當于先定義一個變量&…

Spring Data JPA和分頁

讓我們從支持分頁的經典JPA方法開始。 考慮一個簡單的域類–一個具有名字,姓氏的“成員”。 為了支持在成員列表上進行分頁,JPA方法是支持一種查找器,該查找器將獲取第一個結果(firstResult)的偏移量和要檢索的結果&am…

南陽理工 題目63 小猴子下落

小猴子下落 時間限制:3000 ms | 內存限制:65535 KB 難度:3 描述 有一顆二叉樹,最大深度為D,且所有葉子的深度都相同。所有結點從左到右從上到下的編號為1,2,3,,2的D次方減1。在結點1處放一個小猴子&#…

vue 方法獲取返回值_vue.js - vuex異步提交,怎么獲取返回數據

問 題 做登錄頁面時,在vuex中的action異步提交后獲取的數據在mutations中存儲在state里面,但是總感覺不對,沒有返回數據,我前端頁面怎么獲取數據,用mapgetter獲取不到數據,是不是他不是實時更新的,而且輸出的mapgetter輸出的數據還在action的前面。下面是我前端部分代碼…

Windows環境下安裝、卸載Apache

安裝Apache 服務 打開 Apcahe的目錄 ,打開bin目錄, 如:E:\wamp\Apache24\bin ,打開目錄,Shift鍵 鼠標右鍵 , 點擊 在此處打開命令窗口或者W快捷鍵直接到此處, 也可以Window鍵r,輸入…

css清浮動

我們在平常做項目的時候,float這個css屬性經常會用到。元素浮動會讓元素脫離文檔流,從而不能撐開父級的內容。今天我將展示常見的清除浮動的方法。 什么是浮動 浮動元素脫離文檔流并且向左或者向右移動,直到浮動元素的邊緣碰到父級框或者另…

小心緩存管理器

如果使用spring和JPA,則很有可能利用ehcache(或其他緩存提供程序)。 您可以在兩種不同的情況下進行此操作:JPA 2級緩存和spring方法緩存。 在配置應用程序時,通常會設置JPA提供程序的二級緩存提供程序(在我…

DirectX11 學習筆記7 - 支持自由移動的攝像機

如今將又一次制定一個camera攝像機。能夠自由移動。比方前進 后退,上游 下潛。 各個方向渲染之類的。 首先設置按鍵。 這個時候須要在 XWindow.h 里面 bool XWindow::frame() {//推斷是否按下ESC鍵if(x_input->isKeyDown(VK_ESCAPE))return false;//假設A,S,D,W,…

騰訊吃雞 android,騰訊吃雞手游《光榮使命》正式上線:安卓/iOS不限號測試

IT之家11月29日消息 今天下午,騰訊首款百人戰術競技手游《光榮使命》在安卓、iOS雙平臺正式上線,開啟全面測試。(官網下載:點此鏈接,雙平臺已開放下載。)該游戲采用第三人稱射擊視角,玩家化身參與“使命行動”軍事演習…

lazada鋪貨模式的選品_lazada小白的運營難點→鋪貨與精細化運營的優劣勢詳解

lazada是鋪貨還是精細化經營第一種鋪貨鋪貨作為平臺早期都是比較受歡迎的,平臺的蠻荒期,成長期當中,鋪貨的商家是非常受歡迎的,因為平臺需要更多SKU產品,去吸引買家,鋪貨這個時候是最好的也是能最快的成長起…

ife 零基礎學院 day 2

第二天:給自己做一個在線簡歷吧 最后的驗證,提出了幾個問題,嘗試解答一下 HTML是什么,HTML5是什么 HTML的定義摘抄自w3school的HTML 簡介 HTML 是用來描述網頁的一種語言。 HTML 指的是超文本標記語言 (Hyper Text Markup Langua…

excel數據生成sql?insert語句

excel數據生成sql insert語句 excel表格中有A、B、C三列數據,希望導入到數據庫users表中,對應的字段分別是name,sex,age 。 在你的excel表格中增加一列,利用excel的公式自動生成sql語句,方法如下: 1、增加一列&#xf…

Java中的推斷異常

借用和竊取其他語言的概念和想法總是很高興的。 Scala的Option是我真正喜歡的一個主意,因此我用Java編寫了一個實現。 它包裝了一個可能為null或不為null的對象,并提供了一些可按某種功能使用的方法。 例如,isDefined方法添加了一種面向對象的…

重載,覆蓋,隱藏

轉載于:https://www.cnblogs.com/jhcelue/p/7145525.html

Animate.css介紹

Animate.css簡介 animate.css 動畫庫,預設了抖動(shake)、閃爍(flash)、彈跳(bounce)、翻轉(flip)、旋轉(rotateIn/rotateOut)、淡入淡出&#x…

logstash 吞吐量優化_1002-談談ELK日志分析平臺的性能優化理念

在生產環境中,我們為了更好的服務于業務,通常會通過優化的手段來實現服務對外的性能最大化,節省系統性能開支;關注我的朋友們都知道,前段時間一直在搞ELK,同時也記錄在了個人的博客篇章中,從部署…