JVM——Java 虛擬機是如何加載 Java 類的?

引入

在 Java 世界的底層運作中,類加載機制扮演著一個既神秘又關鍵的角色。它就像是一個精心設計的舞臺幕后 machinery,確保了 Java 程序能夠順利運行。今天,我們就深入探索 Java 虛擬機(JVM)是如何加載 Java 類的。

類加載的背景

Java 語言的一個核心優勢是它的平臺無關性,而這一優勢在很大程度上依賴于 Java 虛擬機(JVM)。JVM 作為一個抽象的規范,定義了一個可以執行 Java 字節碼的環境。這個環境能夠將 Java 字節碼轉換成特定平臺上的機器碼,從而實現了 “一次編寫,到處運行” 的承諾。

Java 程序在運行時,需要將類文件(.class)加載到 JVM 的內存中。這個過程不僅涉及到類文件的讀取,還包括對類的驗證、準備、解析和初始化等一系列復雜的操作。這些步驟確保了類的正確性和安全性,并為類的執行做好準備。

類加載的步驟

類加載過程可以分為以下三個主要階段:加載(Loading)、鏈接(Linking)和初始化(Initialization)。

每個階段都有其獨特的任務和目標。

(一)加載階段

加載階段是類加載過程的起始點。在這個階段,JVM 需要將類的字節碼從各種來源(如本地文件系統、網絡等)讀取進來,并將其轉換為一個 Java 類的表示形式,存放在方法區(Method Area)中。

  1. 字節碼來源:字節碼可以來源于多個渠道,最常見的是由 Java 編譯器生成的 class 文件。除此之外,字節碼也可以在程序運行時動態生成,或者從網絡中獲取(例如在網頁中運行的 Java Applet)。

  2. 類加載器:類加載器(ClassLoader)是加載階段的核心組件。JVM 提供了多個內置的類加載器,包括啟動類加載器(Bootstrap ClassLoader)、擴展類加載器(Extension ClassLoader)和應用類加載器(Application ClassLoader)。每個類加載器都有其特定的職責和加載路徑。

    • 啟動類加載器:負責加載 Java 核心類庫(如 java.lang.Object、java.lang.String 等),這些類位于 JRE 的 lib 目錄下。

    • 擴展類加載器:負責加載 Java 擴展類庫,這些類通常位于 JRE 的 lib/ext 目錄下。

    • 應用類加載器:負責加載應用程序類路徑(classpath)上的類文件。

  3. 雙親委派模型:類加載器采用了雙親委派模型(Parent Delegation Model)。當一個類加載器收到類加載請求時,它會先將請求委托給父類加載器。只有當父類加載器無法完成加載任務時,子類加載器才會嘗試自己加載。這種模型保證了 Java 核心類庫的類總是由啟動類加載器加載,避免了類的多次加載和版本沖突問題。

(二)鏈接階段

鏈接階段的目標是將加載進來的類整合到 JVM 中,使其能夠被虛擬機執行。

鏈接過程分為三個步驟:驗證、準備和解析。

  1. 驗證:驗證階段確保加載的類信息符合 JVM 的規范,并且不會危害虛擬機的安全。這一步驟對類的字節碼進行嚴格檢查,包括文件格式驗證、元數據驗證、字節碼驗證和符號引用驗證。

    • 文件格式驗證:檢查類文件的格式是否正確,例如魔數是否正確、版本號是否受支持等。

    • 元數據驗證:驗證類的元數據(如字段、方法、訪問修飾符等)是否符合語義規范。

    • 字節碼驗證:分析字節碼指令,確保它們不會執行非法操作,如非法跳轉、類型轉換錯誤等。

    • 符號引用驗證:確保解析動作能正確執行,即符號引用所指向的類、字段、方法確實存在。

  2. 準備:準備階段為類的靜態變量分配內存,并設置其初始值。這些初始值通常是該類型的默認值(如整數類型的默認值為 0,布爾類型的默認值為 false 等)。在這個階段,JVM 還會為類的字段、方法等創建數據結構,以便后續的訪問和操作。

  3. 解析:解析階段將類、接口、字段和方法的符號引用轉換為直接引用。符號引用是以符號形式表示的類、接口、字段或方法的名稱和描述符等信息。直接引用則是指向內存中具體位置的指針或句柄,可以直接訪問目標數據。解析過程包括對類或接口、字段、方法和接口方法的解析。

(三)初始化階段

初始化階段是類加載過程的最后一步。在這個階段,JVM 執行類構造器(<clinit>() 方法),對類的靜態變量進行初始化操作。類的初始化是按照 Java 代碼的語義進行的,包括對靜態變量的顯式賦值和靜態代碼塊的執行。

  1. 類構造器 <clinit>():類構造器是由編譯器生成的特殊方法,它包含了類的靜態變量初始化代碼和靜態代碼塊中的代碼。JVM 會保證類構造器只被調用一次,并且在多線程環境下是線程安全的。

  2. 初始化觸發條件:類的初始化并不是在類加載完成后立即執行的,而是需要滿足一定的條件才會觸發。以下是一些常見的觸發類初始化的場景:

    • 遇到 new 指令,創建類的實例。

    • 調用類的靜態方法。

    • 訪問類的靜態字段。

    • 子類的初始化會觸發父類的初始化。

    • 使用反射 API 對類進行反射調用。

    • 初始化一個接口時,如果該接口含有 static-initializerdefault 方法,則會觸發接口的初始化。

類加載的實踐示例

接下來,我們通過一個簡單的示例來展示類加載的過程。我們將使用以下代碼片段來演示類的加載和初始化。

public class Singleton {private Singleton() {}
?private static class LazyHolder {static final Singleton INSTANCE = new Singleton();static {System.out.println("LazyHolder.<clinit>");}}
?public static Object getInstance(boolean flag) {if (flag) {return new LazyHolder[2];}return LazyHolder.INSTANCE;}
?public static void main(String[] args) {getInstance(true);System.out.println("----");getInstance(false);}
}

在上述代碼中,我們定義了一個單例類 Singleton,并使用了懶漢式模式的 LazyHolder 內部類來實現延遲初始化。我們可以通過以下步驟來觀察類加載和初始化的過程:

  1. 打印類加載日志:使用 JVM 參數 -verbose:class 來打印類加載的順序。這個參數會告訴 JVM 在控制臺輸出每個類加載的信息。

    java -verbose:class Singleton
  2. 觀察類初始化的觸發時機:在 LazyHolder 內部類的靜態代碼塊中打印特定字樣,以便觀察類初始化的時機。

  3. 修改字節碼并重新加載:使用 jdisjasm 工具對類的字節碼進行反匯編和重新匯編,觀察修改后的類加載和初始化行為。

拓展

自定義類加載器

除了 JVM 提供的默認類加載器外,我們還可以創建自定義類加載器來實現特殊的類加載需求。自定義類加載器可以實現以下功能:

  • 對類文件進行加密和解密,以保護代碼不被輕易篡改或竊取。

  • 動態生成類字節碼,實現運行時類的動態加載。

  • 加載來自網絡或其他非傳統來源的類文件。

自定義類加載器通過繼承 java.lang.ClassLoader 類并重寫 findClass 方法來實現自定義的類加載邏輯。

public class CustomClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 自定義類加載邏輯byte[] classData = loadClassData(name);if (classData == null) {throw new ClassNotFoundException();}return defineClass(name, classData, 0, classData.length);}
?private byte[] loadClassData(String name) {// 實現類數據的加載邏輯// 例如從文件系統、網絡或加密存儲中讀取類文件return null;}
}

命名空間和類的唯一性

類加載器在 Java 中還提供了一個重要的功能:命名空間隔離。類的唯一性不僅由類的全名(包括包名和類名)決定,還與加載它的類加載器實例相關。這意味著,即使兩個類具有相同的全名,如果它們是由不同的類加載器加載的,JVM 也會將它們視為不同的類。

這一特性在許多應用場景中非常有用,例如:

  • 在 Web 服務器中,不同的 Web 應用程序可以加載相同名稱的類,而不會相互干擾。

  • 在 osgi 等模塊化系統中,類加載器的命名空間隔離機制可以實現模塊之間的版本隔離和依賴管理。

性能優化和工具支持

JVM 提供了豐富的工具來監控和優化類加載過程。

以下是一些常用的工具和參數:

  • -verbose:class:打印類加載的詳細日志,幫助開發者了解類加載的順序和時機。

  • -XX:+TraceClassLoading:輸出類加載的追蹤信息,提供更詳細的類加載調試數據。

  • jstat:監控 JVM 的類加載和卸載統計信息,包括已加載的類數、卸載的類數、內存使用情況等。

  • jvisualvm:一個圖形化工具,可以直觀地顯示 JVM 的運行狀態,包括類加載信息、內存使用情況、線程狀態等。

常見問題

新建數組是否會加載和初始化元素類?

在 Java 中,新建數組(如 new LazyHolder[2])會導致元素類的加載,但不會觸發元素類的初始化。這是因為數組的創建只需要加載元素類的類信息,而不需要立即對數組元素進行初始化。只有當首次主動使用到數組元素類時(如訪問數組元素或調用其靜態方法),才會觸發元素類的初始化。

類加載和鏈接的觸發時機

類的加載和鏈接過程并不是在類被首次使用時才發生。實際上,類的加載可能在以下幾種情況下被觸發:

  • 當類作為 Java 應用程序的主類時,會在程序啟動時被加載。

  • 當類被用作父類且子類被初始化時,父類會被加載和鏈接。

  • 當類被用作接口的實現類且接口被初始化時,類會被加載和鏈接。

鏈接過程中的驗證、準備和解析步驟通常在類加載之后立即進行,但在某些情況下,解析步驟可能會延遲到首次使用相關符號引用時才執行。

如何避免類加載的性能瓶頸?

在大型 Java 應用中,類加載過程可能會成為性能瓶頸,尤其是在應用啟動階段。以下是一些優化類加載性能的建議:

  • 減少不必要的依賴:清理項目的類路徑,移除未使用的庫和類文件,可以減少類加載的數量和時間。

  • 優化類加載器的層次結構:合理設計類加載器的層次結構,避免過多的類加載器層級和復雜的委派鏈,可以提高類加載的效率。

  • 使用類預加載技術:對于一些關鍵類或頻繁使用的類,可以在應用啟動時提前加載,避免在運行時動態加載導致的延遲。

  • 監控和分析類加載過程:使用 JVM 提供的監控工具(如 jstatjvisualvm 等)來分析類加載的性能瓶頸,根據實際情況進行優化。

總結

Java 虛擬機的類加載機制是 Java 平臺無關性和安全性的基石。通過加載、鏈接和初始化三個階段,JVM 將類文件轉換為內存中的類表示,并確保類的正確性和安全性。深入了解類加載的過程,不僅可以幫助我們更好地理解 Java 語言的底層運作機制,還能在實際開發中優化類加載性能,解決類加載相關的問題。

在實際應用中,掌握類加載機制的細節對于構建高效、可靠的 Java 應用至關重要。通過合理利用 JVM 提供的類加載器和工具,我們可以更好地管理類的加載過程,提升應用的性能和穩定性。希望本文能夠為你深入探索 Java 虛擬機的類加載機制提供有價值的參考和指導。

如果你在類加載過程中遇到任何問題,或者對本文有任何疑問或建議,歡迎在評論區留言交流。讓我們一起深入學習,共同進步!

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

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

相關文章

清華團隊提出時序聚類數據庫內高效方案,已被SIGMOD 2025接收

時間序列聚類是挖掘物聯網等場景下頻繁模式的關鍵技術&#xff0c;但現有SOTA方法&#xff08;如K-Shape&#xff09;面臨兩大瓶頸&#xff1a;1&#xff09;傳統數據庫因LSM-Tree存儲導致時間戳無序&#xff0c;難以直接支持高效聚類&#xff1b;2&#xff09;跨時間范圍查詢需…

【阿里云大模型高級工程師ACP學習筆記】2.8 部署模型

一、學習目標 特別說明:這一章節是2025年3月官方重點更新的部分,幾乎對內容重新翻新改造了一遍,重點突出了對于如何結合不同的阿里云產品來部署大模型進行了更加詳細的介紹和對比,這里整理給大家,方便大家參考。 在備考阿里云大模型高級工程師ACP認證的過程中,學習《2.8 …

第T10周:數據增強

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 從 tensorflow.keras 中導入 layers 模塊&#xff0c;包含了常用的神經網絡層&#xff0c;用來搭建模型結構。 檢查并列出系統中可用的物理 GPU 設備&#xff…

uniapp 支付寶小程序自定義 navbar 無效解決方案

如圖&#xff1a; uniapp編譯到支付寶小程序隱藏默認的導航欄失效了 解決方案&#xff1a; 在 pages.json 文件中找到 globalStyle 中加入以下代碼&#xff1a; "mp-alipay": {"transparentTitle": "always","titlePenetrate":…

vue2 el-element中el-select選中值,數據已經改變但選擇框中不顯示值,需要其他輸入框輸入值才顯示這個選擇框才會顯示剛才選中的值

項目場景&#xff1a; <el-table-column label"稅率" prop"TaxRate" width"180" align"center" show-overflow-tooltip><template slot-scope"{row, $index}"><el-form-item :prop"InquiryItemList. …

centos7 離線安裝python3 保留python2

一、事前準備&#xff1a; &#xff08;1&#xff09;查看centos具體版本 cat /etc/redhat-releaseCentOS Linux release 7.4.1708 (Core) &#xff08;2&#xff09;查看linux中當前python版本 centos7 默認安裝python2.7.5 &#xff08;3&#xff09;查看python3的依賴&#…

十三種通信接口芯片——《器件手冊--通信接口芯片》

目錄 通信接口芯片 簡述 基本功能 常見類型 應用場景 詳盡闡述 1 RS485/RS422芯片 1. RS485和RS422標準 2. 芯片功能 3. 典型芯片及特點 4. 應用場景 5. 設計注意事項 6. 選型建議 2 RS232芯片 1. RS232標準 2. 芯片功能 3. 典型芯片及特點 4. 應用場景 5. 設計注意事項 6…

2025年RAG技術發展現狀分析

2025年&#xff0c;大模型RAG&#xff08;檢索增強生成&#xff09;技術經歷了快速迭代與深度應用&#xff0c;逐漸從技術探索走向行業落地&#xff0c;同時也面臨安全性和實用性的新挑戰。以下是其發展現狀的綜合分析&#xff1a; 一、技術架構的持續演進 從單一到模塊化架構 …

case和字符串操作

使用if選擇結構 if [];then elif [];then #注意這個地方,java是else if else ; fi 使用for循環結構 使用for循環&#xff0c;語法結構如下所示&#xff1a; for 變量名 in 值1 值2 值3 #值的數量決定循環任務的次數 do命令序列 done#循環輸出1到10 for i in {1..10} #注…

Stm32 燒錄 Micropython

目錄 前言 準備工作 開始操作 問題回顧 后記 前言 去年曾經嘗試Pico制作openmv固件&#xff0c;由于知識儲備不夠最后失敗了&#xff0c;留了一個大坑&#xff0c;有了前幾天的基礎&#xff0c;慢慢補齊知識&#xff0c;最近這一周一直在學習如何編譯Stm固件并燒錄到單片機…

鹽化行業數字化轉型規劃詳細方案(124頁PPT)(文末有下載方式)

資料解讀&#xff1a;《鹽化行業數字化轉型規劃詳細解決方案》 詳細資料請看本解讀文章的最后內容。 該文檔聚焦鹽化行業數字化轉型&#xff0c;全面闡述了鹽化企業信息化建設的規劃方案&#xff0c;涵蓋戰略、架構、實施計劃、風險及效益等多個方面&#xff0c;旨在通過數字化…

2025年人工智能火爆技術總結

2025年人工智能火爆技術總結&#xff1a; 生成式人工智能 生成式人工智能可生成高質量的圖像、視頻、音頻和文本等多種內容。如昆侖萬維的SkyReels-V2能生成無限時長電影&#xff0c;其基于擴散強迫框架&#xff0c;結合多模態大語言模型和強化學習等技術&#xff0c;在運動動…

邊緣計算革命:大模型輕量化部署全棧實戰指南

當ResNet-152模型能在樹莓派4B上實現每秒27幀實時推理時&#xff0c;邊緣智能時代真正到來。本文解析從模型壓縮到硬件加速的完整技術棧&#xff0c;實測Transformer類模型在移動端的部署時延可壓縮至16ms&#xff0c;揭示ARM芯片實現INT4量化的工程秘訣與十種典型場景優化方案…

邊緣計算:數字世界的”末梢神經系統”解析-優雅草卓伊凡

邊緣計算&#xff1a;數字世界的”末梢神經系統”解析-優雅草卓伊凡 一、邊緣計算深度解析 1.1 邊緣計算的定義與架構 邊緣計算&#xff08;Edge Computing&#xff09;是一種分布式計算范式&#xff0c;它將數據處理能力從傳統的集中式云數據中心推向網絡邊緣&#xff0c;更…

面試手撕——迭代法中序遍歷二叉樹

思路 訪問順序和處理順序不一致導致迭代法難寫&#xff0c;體現在總要先遍歷根節點&#xff0c;才能訪問左右孩子&#xff0c;用null標記&#xff0c;null標記的節點表示已經訪問過了&#xff0c;下一次可以處理&#xff0c;所以在當前棧頂節點不是null的時候&#xff0c;都要…

AD系列:Windows Server 2025 安裝AD CS角色和頒發證書

什么是 Active Directory 證書服務&#xff1f; Active Directory 證書服務 (AD CS) 是一個 Windows Server 角色&#xff0c;負責頒發和管理在安全通信和身份驗證協議中使用的公鑰基礎結構 (PKI) 證書。 頒發和管理證書 數字證書可用于對電子文檔和消息進行加密和數字簽名&…

kubernetes》》k8s》》Service 、Ingress 區別

K8S>>Service 資料 K8S >>Ingress 資料 Ingress VS Service 物理層數據鏈路層網絡層傳輸層會話層表示層應用層 Ingress是一種用于暴露HTTP和HTTPS路由的資源&#xff0c;它提供了七層&#xff08;應用層&#xff09;的負載均衡功能。Ingress可以根據主機名、…

【java WEB】恢復補充說明

Server 出現javax.servlet.http.HttpServlet", according to the project’s Dynamic Web Module facet version (3.0), was not found on the Java Build Path. 右鍵項目 > Properties > Project Facets。Dynamic Web Module facet version選4.0即可 還需要在serv…

VMware 創建虛擬機+簡易安裝Ubuntu的詳細操作步驟

VMware 創建虛擬機安裝Ubuntu的詳細操作步驟 一、創建虛擬機1.1 點擊創建新的虛擬機1.2 選擇自定義創建虛擬機1.3 選擇虛擬機的硬件兼容性1.4 安裝客戶機操作系統1.5 簡易安裝信息1.6 命名虛擬機名稱1.7 處理器配置1.8 虛擬機內核選擇1.9 網絡類型1.9 選擇I/O 控制器類型1.10 選…

GCC-C語言“自定義段”

一、起因 事情的起因是這樣的,在看別人代碼時,發現了一種很有意思的寫法,因為本人主要是以應用層開發為主,所以對這種寫法還是比較少見的,所以研究了一下,就牽扯出了一些知識點,這里先賣個關子,繼續往下看。 二、經過 發現了一串這樣的代碼 static void do_mac(mcmd_…