java虛擬機之類加載《JAVA虛擬機》要點精煉


常量池主要存放兩大類常量:字面量符號引用。字面量是java意義中的常量,諸如final,或文本字符串。而符號引用則是編譯原理中的概念,主要包括:

  • 類和接口的全限定名(com/enjoy/learn/core/oop/method/TestClass(理解爲加上包名的完整名字))
  • 字段的名稱和描述符
    描述符的含義:描述字段的數據類型,比如char就是C
  • 方法的名稱和描述符
    描述符的含義:描述方法的參數列表與返回值,比如void inc() -> ()V

類加載機制

類加載的流程

類加載機制是把描述類的數據class文件加載至內存,並對數據進行校驗轉換解析初始化,最終形成可以被虛擬機直接使用的java類型。

特性:類型的加載、連接和初始化過程都是在程序運行期完成的,從而通過犧牲一些性能開銷來換取Java程序的高度靈活性。

0.類加載的時機

類從加載進內存開始,到卸載出內存爲止,共經歷七個生命週期:加載、驗證、準備、解析、初始化、使用、卸載。
在這裏插入圖片描述
其中加載、驗證、準備、初始化的順序是確定的,而解析則不確定,可以再初始化後進行解析。

以下五種情況對類進行初始化:
(1)遇到new、getstatic、setstatic、invokestatic;當new對象,或讀取設置調用一個靜態字段,以及調用一個類的靜態方法時;
(2)使用反射時,若類沒有進行初始化,則會進行初始化;
(3)當初始化一個類時,如果其父類沒有初始化,則會先對其父類進行初始化;
(4)當虛擬機啓動時,用戶需要指定一個主類(包含main方法的),該類要先進行初始化

1.加載

加載包括三個步驟:

  • 首先通過類對應的全限定名找到二進制字節流
  • 將字節流代表的靜態結構轉換爲方法區的運行時數據結構
  • 在內存中生成一個代表該類的class對象,作爲方法區中這個類的各種數據的訪問入口。

2.驗證

驗證是爲了保證class文件中的二進制字節流符合jvm的規範。

  1. 文件格式驗證字節流是否符合class文件的規範
  2. 元數據驗證:判斷字節流中的類是否滿足規範,比如是否具有父類,其父類是否繼承了不允許繼承的類(final),是對字節流中的數據類型進行校驗
  3. 字節碼驗證對類的方法體進行校驗分析。
    4.符號引用驗證字節流中的符號引用是否正確;字段或方法是否引用了當前類不能使用的方法或類。

3.準備

爲類對象(靜態變量)分配內存並初始化。初始化是賦零值,只有在flinit之後纔會有具體的值。

4.解析

解析階段是虛擬機將常量池內的符號引用轉換成直接引用

符號引用:符號引用以一組符號來描述所引用的目標,只要使用時能無歧義地定位到目標即可,引用的目標不一定加載到內存中。

直接引用:直接引用是指直接指向對象的指針,或指向一個句柄,引用的目標一定加載到內存中。

虛擬機並未規定解析的時間:需要來判斷到底是在類被加載器加載時就對常量池中的符號引用進行解析, 還是等到一個符號引用將要被使用前纔去解析它

5.初始化

初始化才真正開始執行類中定義的Java程序代碼( 或者說是字節碼)

在準備時,已經爲對象賦了一次初始值,而初始化中將類對象按照程序要求的進行賦值。初始化階段是執行flinit方法。

類加載器

類加載器的種類

java中將通過類的全限定名來獲取二進制字節流拿到jvm外部執行

類的加載十分重要,對於任意一個類, 都需要由加載它的類加載器和這個類本身一同確立其在Java虛
擬機中的唯一性
, 每一個類加載器, 都擁有一個獨立的類名稱空間。通俗來說,比較兩個類是否“相等”, 只有在這兩個類是由同一個類加載器加載的前提下纔有意義,比如equals方法。

從虛擬機的角度一共有兩種虛擬機:啓動類加載器,由C++編寫,是虛擬機的一部分,不可以被java代碼使用;其他類加載器:由java編寫,獨立於虛擬機外,都繼承自抽象類java.lang.ClassLoader

具體爲三種類加載器:

  1. 啓動類加載器

    • 由C++編寫,是虛擬機自身的一部分
    • 該加載器負責加載<JAVA_HOME>/lib 下以及或被-Xbootclasspath參數所指定路徑中的、且可被虛擬機識別的類庫
    • 啓動類加載器不可以直接被java代碼所引用
  2. 拓展類加載器:

    • 該加載器加載<JAVA HOME>/lib/ext或者被**java.ext.dirs系統變量**所指定的路徑中的所有類庫。
  3. 應用程序類加載器

    • 該加載器加載用戶路徑classpath下的所有類,該加載器在java中;
    • 如果應用程序中沒有自定義過自己的類加載器, 一般情況下這個就是程序中默認的類加載器

雙親委派機制

頂層爲啓動類加載器、擴展類加載器、應用程序類加載器、自定義的類加載器。
在這裏插入圖片描述
除了頂層啓動類加載器外,都應當有自己的父類加載器,這裏的父與子的關係不是繼承而是組合關係來複用父加載器的代碼。

工作流程:當類加載器收到一個加載類的通知後,不會自己加載類,而是上交給父類加載器處理,當父類加載器不能加載時,所查找的區域沒有該類,則會交由子類加載器加載。

好處:以java.lang.Object爲例,該類應該在啓動類加載器加載,這樣Object類在各個類加載器中都是同一個類。如果沒有雙親委派機制的話,就會在子類加載器加載,假如應用程序的classpath中也有一個java.lang.Object就會導致,Object類被多次加載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章