JVM虛擬機類加載機制的學習

前言:學習JVM是一個漫長的課程,在你學的過程中,JVM的學習過程中是枯燥的乏味的,不要想着一口喫一個胖子,需要沉下心,一步一步來。

JVM是一個令人望而卻步的領域,因爲它博大精深,涉及到的內容與知識點非常之多。雖然Java開發者每天都在使用JVM,但對其有所研究並且研究深入的人卻少之又少。然而,JVM的重要性卻又是不言而喻的。基於JVM的各種動態與靜態語言生態圈已經異常繁榮了,對JVM的運行機制有一定的瞭解不但可以提升我們的競爭力,還可以讓我們在面對問題時能夠沉着應對,加速問題的解決速度;同時還能夠增強我們的自信心,讓我們更加遊刃有餘。

本人也是剛學JVM,想要把JVM學好,最起碼能夠應付面試和讓自己對程序的理解更上一個臺階。我這次打算從jvm的以下方面進行學習:

  • JVM介紹
  • HotSpot虛擬機講解
  • 垃圾收集方式詳解
  • 垃圾收集算法詳解
  • 垃圾收集器詳解
  • 分代垃圾收集機制詳解
  • 新生代講解
  • 老年代講解
  • G1收集器分析與實例
  • 常見且重要虛擬機參數示例
  • 方法區
  • 線程共享內存區
  • 根搜索算法
  • Serial收集器
  • ParNew收集器
  • 類加載機制詳解
  • 類加載的雙親委託機制
  • 字節碼文件生成與分析
  • 魔數
  • 常量池與方法表
  • 各種指令詳解
  • 鎖詳解
  • 線程安全
  • 偏向鎖、自旋鎖與輕量級鎖
  • JIT編譯器
  • GC日誌生成與分析
  • 虛擬機監控工具詳解
  • jConsole使用方式詳解
  • 何爲逃逸與逃逸分析
  • 方法內聯
  • 虛擬機內存模型詳解

這些東西在深入理解Java虛擬機上面其實是都有用的,但是我不會按照上面羅列的來介紹。我會按照我自己的理解來完善我這個系列博客的。

1.類加載

在java的代碼中,類型的加載連接初始化的過程都是在程序運行期間完成的

加載:查找並加載二進制數據

連接:

        驗證:確保加載類的正確性

        準備:爲類的靜態變量分配內存,將其初始化爲默認值

        解析:把類中的符號引用轉化爲直接引用

初始化:爲類的靜態變量賦予正確的初始值

類加載的五個階段:

java程序對類的使用方式有兩種,主動使用和被動使用

所有的java虛擬機實現必須在每個類或者接口被Java程序“首次主動使用”時才初始化他們

何爲主動使用(七種方式):

1.創建類的實例

2.訪問某個類或者接口的靜態變量,或者對該靜態變量賦值

3.調用類的靜態方法

4.反射

5.初始化一個類的子類

6.java虛擬機啓動被標明啓動類的類

7.jdk1.7開始提供的動態語言支持(瞭解)

除了這七種情況,其他使用java類的方式都被看做是對類的被動使用,都不會導致類的初始化

舉一個例子來分析下啥是類的主動和被動加載

下面我們可以把子類的靜態變量改爲靜態常量

我們可以看出來,這一次並沒有執行父類的靜態構造,這是爲什麼呢???

原因:在編譯階段,把final定義的常量放到了調用這個常量的方法所在類的常量池中(這裏就放在了MyTest的常量池當中)

我們下面把代碼稍微的改東下,我們再看下輸出的結果

原因:當一個常量的值並非編譯期間能夠確定的,那麼其值就不會放到調用類的常量池中,這是程序在運行的時候,會導致主動使用這個常量所在的類,會導致這個類被初始化

下面我們來分析下父子類接口的加載關係:

首先我們要先把結論說出來:當一個接口被初始化的時候,並不要求他的父類完成初始化,這一點是和類是不一樣的

爲什麼呢:這個案例其實不是很好舉的,你在接口的定義的變量其實都是被final修飾的,他們都會被放到調用類的常量池裏面,從而不會加載父類接口。

我們下面來分析一下準備階段和初始化順序的問題(面試的時候,筆試題可能會有這種類型的題,很坑哦)

我們來根據類的準備和初始化階段來分析下這段代碼的執行順序

這一切的結果應該和很多人的想法一致,那麼我們現在把,Single類裏面的b的定義放在構造方法下面,我們再來分析一下這個結果和執行情況

有兩種類型的類加載器
Java虛擬機自帶的加載器
根類加載器(Bootstrap):該加載器沒有父加載器,它負責加載虛擬機中的核心類庫。根類加載器從系統屬性sun.boot.class.path所指定的目錄中加載類庫。類加載器的實現依賴於底層操作系統,屬於虛擬機的實現的一部分,它並沒有集成java.lang.ClassLoader類。
擴展類加載器(Extension):它的父加載器爲根類加載器。它從java.ext.dirs系統屬性所指定的目錄中加載類庫,或者從JDK的安裝目錄的jre\lib\ext子目錄(擴展目錄)下加載類庫,如果把用戶創建的jar文件放在這個目錄下,也會自動由擴展類加載器加載,擴展類加載器是純java類,是java.lang.ClassLoader的子類。
系統應用類加載器(System):也稱爲應用類加載器,它的父加載器爲擴展類加載器,它從環境變量classpath或者系統屬性java.class.path所指定的目錄中加載類,他是用戶自定義的類加載器的默認父加載器。系統類加載器時純java類,是java.lang.ClassLoader的子類。
用戶自定義的類加載器
java.lang.ClassLoader的子類
用戶可以定製類的加載方式
根類加載器–>擴展類加載器–>系統應用類加載器–>自定義類加載器
類加載器並不需要等到某個類被“首次主動使用”時再加載它

JVM規範允許類加載器在預料某個類將要被使用時就預先加載它,如果在預先加載的過程中遇到了.class文件缺失或存在錯誤,類加載器必須在程序首次主動使用該類才報告錯誤(LinkageError錯誤),如果這個類沒有被程序主動使用,那麼類加載器就不會報告錯誤。

類加載器用來把類加載到java虛擬機中。從JDK1.2版本開始,類的加載過程採用父親委託機制,這種機制能更好地保證Java平臺的安全。在此委託機制中,除了java虛擬機自帶的根類加載器以外,其餘的類加載器都有且只有一個父加載器。當java程序請求加載器loader1加載Sample類時,loader1首先委託自己的父加載器去加載Sample類,若父加載器能加載,則有父加載器完成加載任務,否則才由加載器loader1本身加載Sample類。
 

 

由於本人工作的原因,最近java原理這個專題可能要延後一階段才能更新,但是java原理這個章節的內容我肯定會慢慢完善的。

 

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