JVM之類的生命周期(二)
- 2019 年 10 月 8 日
- 筆記
我們都知道,在Java當中,JVM為Java程序提供運行時環境,其中一項重要的任務就是管理類和對象的生命周期。類的生命周期,簡單來說,是從類被加載、連接和初始化開始,到類被卸載結束。
當類處於生命周期中時,他的二進制數據位於方法區內,在堆區內還會有一個相應的描述這個類的Class對象。只有當類處於生命周期中時,Java程序才能使用它,比如調用類的靜態屬性和方法,或創建類的實例對象。
當啟動一個Java進程時,JVM進程從啟動、到終止,稱為Java虛擬機的生命周期。
1、類的加載、連接和初始化
a、加載
I、通過一個類的全限定名來獲取其定義的二進制位元組流。
II、將這個位元組流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
III、在Java堆中生成一個代表這個類的 java.lang.Class 對象,作為對方法區中這些數據的訪問入口。
b、連接
I、驗證:確保被加載的類的正確性
II、準備:為類的靜態變量分配內存,並將其初始化為默認值
默認初始值如下:
1.八種基本數據類型默認的初始值是0
2.引用類型默認的初始值是null
3.有static final修飾的會直接賦值,
例如:static final int x=10;則默認就是10.
III、解析:把類中的符號引用轉換為直接引用
c、初始化
I、假如這個類還沒有被加載和連接,則程序先加載並連接該類
II、假如該類的直接父類還沒有被初始化,則先初始化其直接父類
III、假如類中有初始化語句,則系統依次執行這些初始化語句
類的初始化時機是什麼?
JVM只有在程序首次主動使用一個類或者接口時才會初始化。有6種活動被看做是程序對類或接口的主動使用。
1、創建類的實例,可以通過new、反射、clone、反序列化的方式。
2、調用類的靜態方法。
3、訪問某個類或接口的靜態變量,或者對該靜態變量賦值。
4、調用Java API種某些反射方法,比如Class.forName("BboyHan"),假如BboyHan類還沒有被初始化,那麼forName()方式是java.lang.Class類的靜態方法。
5、初始化一個類的子類。可以看做是它對父類(或者稱為基類)的主動使用,因此會先初始化父類。
6、JVM啟動時被標明為啟動類的類。如「java Sample」命令,Sample類就是啟動類。
除了以上6種情形,其他使用Java類的方式都被看做是被動使用,都不會導致類的初始化。
d、使用
只有當對類的主動使用的時候才會導致類的初始化,類的主動使用包括以下六種:
I、創建類的實例,也就是new的方式
II、訪問某個類或接口的靜態變量,或者對該靜態變量賦值
III、調用類的靜態方法
IV、反射(如 Class.forName(「com.shengsiyuan.Test」))
V、初始化某個類的子類,則其父類也會被初始化
VI、Java虛擬機啟動時被標明為啟動類的類( JavaTest),直接使用 java.exe命令來運行某個主類
e、卸載
I、執行了 System.exit()方法
II、程序正常執行結束
III、程序在執行過程中遇到了異常或錯誤而異常終止
IV、由於操作系統出現錯誤而導致Java虛擬機進程終止
2、類加載器
一個類在JVM當中又是如何被加載並使用的呢?
類加載器(class loader)用來加載 Java 類到 Java 虛擬機中。一般來說,Java 虛擬機使用 Java 類的方式如下:Java 源程序(.java 文件)在經過 Java 編譯器編譯之後就被轉換成 Java 位元組代碼(.class 文件)。
1、當Java程序需要使用某個類時,JVM會確保這個類已經被加載、連接(驗證、準備和解析)和初始化:
2、類的加載是指把類的.class文件中的數據讀入到內存中,通常是創建一個位元組數組讀入.class文件,然後產生與所加載類對應的Class對象。加載完成後,Class對象還不完整,所以此時的類還不可用。
3、當類被加載後就進入連接階段,這一階段包括驗證、準備(為靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換為直接引用)三個步驟。
它有兩種裝載class的方式:顯式和隱式
隱式:運行過程中,碰到new方式生成對象時,隱式調用classLoader到JVM
顯式:通過class.forname()動態加載
類加載器負責讀取 Java 位元組代碼,並轉換成 java.lang.Class類的一個實例。每個這樣的實例用來表示一個 Java 類。通過此實例的 newInstance()方法就可以創建出該類的一個對象。實際的情況可能更加複雜,比如 Java 位元組代碼可能是通過工具動態生成的,也可能是通過網絡下載的。
加載類的過程:採用雙親委託機制(雙親委派模型)
雙親委派模型的工作過程:
a、當前 ClassLoader 首先從自己已經加載的類中查詢是否此類已經加載,如果已經加載則直接返回原來已經加載的類。每個類加載器都有自己的加載緩存,當一個類被加載了以後就會放入緩存,等下次加載的時候就可以直接返回了。
b、當前 classLoader 的緩存中沒有找到被加載的類的時候,委託父類加載器去加載,父類加載器採用同樣的策略,首先查看自己的緩存,然後委託父類的父類去加載,一直到 bootstrap ClassLoader.
c、當所有的父類加載器都沒有加載的時候,再由當前的類加載器加載,並將其放入它自己的緩存中,以便下次有加載請求的時候直接返回。
思考:如何判定兩個 Java 類是相同的?
Java 虛擬機不僅要看類的全名是否相同,還要看加載此類的類加載器是否一樣。只有兩者都相同的情況,才認為兩個類是相同的。即便是同樣的位元組代碼,被不同的類加載器加載之後所得到的類,也是不同的。因此,在判斷是否為同一個Java類時,必須是由同一個類加載器所加載。
End……