日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

怎么加载文件_Java虚拟机从入门到入土之JVM的类加载机制

發(fā)布時間:2025/3/12 java 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 怎么加载文件_Java虚拟机从入门到入土之JVM的类加载机制 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者:六脈神劍

轉(zhuǎn)載于:https://juejin.im/post/5e1aaf626fb9a0301d11ac8e

JVM總體概述

JVM總體上是由

  • 類裝載子系統(tǒng)(ClassLoader)
  • 運行時數(shù)據(jù)區(qū)
  • 執(zhí)行引擎
  • 內(nèi)存回收
  • 類文件結(jié)構(gòu)

以上5個部分組成,每一個都是非常重要的,如果你要了解JVM,要學(xué)習(xí)JVM調(diào)優(yōu),那么只能是一個個去把他們啃了

什么是類加載機制

書上的原話:

虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對這些數(shù)據(jù)進行校驗,轉(zhuǎn)換 解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制

類加載的時機

類從被加載到虛擬機內(nèi)存中開始,到卸載出內(nèi)存為止,它的生命周期包括

  • 加載
  • 驗證
  • 準(zhǔn)備
  • 解析
  • 初始化
  • 使用
  • 卸載

總共是7個階段

理解類加載三個字

首先 類 是指的.Class文件類,那么怎么生成這個文件呢?

  • Java代碼編譯
  • 原本就是.Class 文件
  • 動態(tài)代理生成

等等 還有很多

那么 加載 這2個字應(yīng)該怎么理解呢 大家可以看下圖

本地的.Class文件通過類加載器加載到JVM內(nèi)存中的方法區(qū)里面,然后通過這個對象來訪問數(shù)據(jù)區(qū)的數(shù)據(jù)

  • 通過一個類的全限定名來獲取定義此類的二進制字節(jié)流
  • 將這個字節(jié)流代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化成方法區(qū)的二進制字節(jié)流
  • 再內(nèi)存的方法區(qū)生成這個類的Java.lang.Class對象,作為這個類各個數(shù)據(jù)訪問的入口
  • 五種必須初始化的情況

    Java并沒用規(guī)定生命時候進行類加載的第一階段,但是對于初始化階段,虛擬機有嚴(yán)格的規(guī)范

    • 遇到new 關(guān)鍵字的時候
    • 使用reflect包的方法的時候
    • 當(dāng)初始化一個類的時候發(fā)現(xiàn)父類還沒初始化,必須先初始化父類
    • 當(dāng)虛擬機啟動的時候,加載main方法的類
    • 當(dāng)使用1.7的動態(tài)語言支持的時候(這塊沒有接觸過,有沒有大佬懂的)

    驗證階段

    分為以下幾種樣裝情況

    • 文件格式的驗證,驗證當(dāng)前字節(jié)流是否能被JVM識別
    • 元數(shù)據(jù)的驗證,驗證它的父類,它的繼承,是否是抽象類等
    • 字節(jié)碼驗證,驗證邏輯是否合理
    • 符合引用的驗證 驗證是否能通過生成的Class對象找到對應(yīng)的數(shù)據(jù)

    準(zhǔn)備階段

    準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些內(nèi)存都將在方法區(qū)中分配。對于該階段有以下幾點需要注意:

    1、這時候進行內(nèi)存分配的僅包括類變量(static),而不包括實例變量,實例變量會在對象實例化時隨著對象一塊分配在Java堆中。

    2、這里所設(shè)置的初始值通常情況下是數(shù)據(jù)類型默認(rèn)的零值(如0、0L、null、false等),而不是被在Java代碼中被顯式地賦予的值。

    假設(shè)一個類變量的定義為:

    public static int value = 3;

    那么變量value在準(zhǔn)備階段過后的初始值為0,而不是3,因為這時候尚未開始執(zhí)行任何Java方法,而把value賦值為3的putstatic指令是在程序編譯后,存放于類構(gòu)造器()方法之中的,所以把value賦值為3的動作將在初始化階段才會執(zhí)行。

    下表列出了Java中所有基本數(shù)據(jù)類型以及reference類型的默認(rèn)零值:

    這里還需要注意如下幾點:

    • 對基本數(shù)據(jù)類型來說,對于類變量(static)和全局變量,如果不顯式地對其賦值而直接使用,則系統(tǒng)會為其賦予默認(rèn)的零值,而- 對于局部變量來說,在使用前必須顯式地為其賦值,否則編譯時不通過。
    • 對于同時被static和final修飾的常量,必須在聲明的時候就為其顯式地賦值,否則編譯時不通過;而只被final修飾的常量則既可以在聲明時顯式地為其賦值,也可以在類初始化時顯式地為其賦值,總之,在使用前必須為其顯式地賦值,系統(tǒng)不會為其賦予默認(rèn)零值。
    • 對于引用數(shù)據(jù)類型reference來說,如數(shù)組引用、對象引用等,如果沒有對其進行顯式地賦值而直接使用,系統(tǒng)都會為其賦予默認(rèn)的零值,即null。
    • 如果在數(shù)組初始化時沒有對數(shù)組中的各元素賦值,那么其中的元素將根據(jù)對應(yīng)的數(shù)據(jù)類型而被賦予默認(rèn)的零值。

    如果類字段的字段屬性表中存在ConstantValue屬性,即同時被final和static修飾,那么在準(zhǔn)備階段變量value就會被初始化為ConstValue屬性所指定的值。

    解析階段

    解析階段是虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程 ##初始化階段 到了初始化階段,才真正開始執(zhí)行類中定義的Java代碼

    類加載器

    站在Java虛擬機的角度來講,只存在兩種不同的類加載器:

    • 啟動類加載器:它使用C++實現(xiàn)(這里僅限于Hotspot,也就是JDK1.5之后默認(rèn)的虛擬機,有很多其他的虛擬機是用Java語言實現(xiàn)的),是虛擬機自身的一部分。
    • 所有其他的類加載器:這些類加載器都由Java語言實現(xiàn),獨立于虛擬機之外,并且全部繼承自抽象類java.lang.ClassLoader,這些類加載器需要由啟動類加載器加載到內(nèi)存中之后才能去加載其他的類。

    站在Java開發(fā)人員的角度來看,類加載器可以大致劃分為以下四類:

    • 啟動類加載器 (C實現(xiàn))
    • 擴展類加載器 (ClassLoader)
    • 應(yīng)用程序加載器 (ClassLoader)
    • 自定義加載器 (ClassLoader)

    這幾種類加載器的層次關(guān)系如下圖所示:

    雙親委派模型

    類加載器之間的這種層次關(guān)系叫做雙親委派模型。 雙親委派模型要求除了頂層的啟動類加載器(Bootstrap ClassLoader)外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。這里的類加載器之間的父子關(guān)系一般不是以繼承關(guān)系實現(xiàn)的,而是用組合實現(xiàn)的。

    雙親委派模型的工作過程

    由我來概況就是 八個字 向上檢查,從下加載 如果一個類接受到類加載請求,他自己不會去加載這個請求,而是將這個類加載請求委派給父類加載器,這樣一層一層傳送,直到到達啟動類加載器(Bootstrap ClassLoader)。 只有當(dāng)父類加載器無法加載這個請求時,子加載器才會嘗試自己去加載。

    雙親委派模型的代碼實現(xiàn)

    雙親委派模型的代碼實現(xiàn)集中在java.lang.ClassLoader的loadClass()方法當(dāng)中。

    • 首先檢查類是否被加載,沒有則調(diào)用父類加載器的loadClass()方法;
    • 若父類加載器為空,則默認(rèn)使用啟動類加載器作為父加載器;
    • 若父類加載失敗,拋出ClassNotFoundException 異常后,再調(diào)用自己的findClass() 方法。 loadClass源代碼如下:
    protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException { //1 首先檢查類是否被加載 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //2 沒有則調(diào)用父類加載器的loadClass()方法; c = parent.loadClass(name, false); } else { //3 若父類加載器為空,則默認(rèn)使用啟動類加載器作為父加載器; c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { //4 若父類加載失敗,拋出ClassNotFoundException 異常后,這個方法就是加載的核心代碼 c = findClass(name); } } if (resolve) { //5 再調(diào)用自己的findClass() 方法。 resolveClass(c); } return c;}

    自定義類加載器

    class NetworkClassLoader extends ClassLoader { * String host; * int port; * * public Class findClass(String name) { * byte[] b = loadClassData(name); * return defineClass(name, b, 0, b.length); * } * private byte[] loadClassData(String name) { // load the class data from the connection . . . } }

    這個就是官方的例子

    破環(huán)雙親委派

    雙親委派模型很好的解決了各個類加載器加載基礎(chǔ)類的統(tǒng)一性問題。即越基礎(chǔ)的類由越上層的加載器進行加載。 若加載的基礎(chǔ)類中需要回調(diào)用戶代碼,而這時頂層的類加載器無法識別這些用戶代碼,怎么辦呢?這時就需要破壞雙親委派模型了。

    java默認(rèn)的線程上下文類加載器是系統(tǒng)類加載器(AppClassLoader).

    // Now create the class loader to use to launch the application try { loader = AppClassLoader.getAppClassLoader(extcl); } catch (IOException e) { throw new InternalError( "Could not create application class loader" ); } // Also set the context class loader for the primordial thread. Thread.currentThread().setContextClassLoader(loader);

    以上代碼摘自sun.misc.Launch的無參構(gòu)造函數(shù)Launch()。

    使用線程上下文類加載器,可以在執(zhí)行線程中,拋棄雙親委派加載鏈模式,使用線程上下文里的類加載器加載類.

    典型的例子有,通過線程上下文來加載第三方庫jndi實現(xiàn),而不依賴于雙親委派.

    大部分java app服務(wù)器(jboss, tomcat..)也是采用contextClassLoader來處理web服務(wù)。

    結(jié)尾

    今天把類加載機制好好講了一下,這樣大家就更加的熟悉了內(nèi)的加載過程,對于Java開發(fā)是有好處的

    總結(jié)

    以上是生活随笔為你收集整理的怎么加载文件_Java虚拟机从入门到入土之JVM的类加载机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。