類的生命周期
類從被加載到虛拟機記憶體中開始,到解除安裝出記憶體為止,它的整個生命周期包括:加載,驗證,準備,解析,初始化,使用,解除安裝這7個階段.其中其中驗證、準備、解析3個部分統稱為連接配接.
加載、驗證、準備、初始化和解除安裝這五個階段的順序是确定的,類型的加載過程必須按照這種順序按部就班地開始,而解析階段則不一定:它在某些情況下可以在初始化階段之後再開始,這是為了支援Java語言的運作時綁定特性(也稱為動态綁定或晚期綁定)
注意,這裡的幾個階段是按順序開始,而不是按順序進行或完成,因為這些階段通常都是互相交叉地混合進行的,通常在一個階段執行的過程中調用或激活另一個階段。
加載:查找并加載類的二進制資料
在加載階段,虛拟機需要完成以下3件事情:
- 1)通過一個類的全限定名來擷取定義此類的二進制位元組流。
- 2)将這個位元組流所代表的靜态存儲結構轉化為方法區的運作時資料結構。
- 3)在記憶體中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種資料的通路入口。
驗證:確定被加載的類的正确性
驗證是連接配接階段的第一步,這一階段的目的是為了確定Class檔案的位元組流中包含的資訊符合目前虛拟機的要求,并且不會危害虛拟機自身的安全。驗證階段大緻會完成4個階段的檢驗動作:
- 檔案格式驗證: 驗證位元組流是否符合Class檔案格式的規範;例如: 是否以
開頭、主次版本号是否在目前虛拟機的處理範圍之内、常量池中的常量是否有不被支援的類型。0xCAFEBABE
- 中繼資料驗證:對位元組碼描述的資訊進行語義分析(注意: 對比
編譯階段的語義分析),以保證其描述的資訊符合Java語言規範的要求;例如: 這個類是否有父類,除了javac
之外。java.lang.Object
- 位元組碼驗證:通過資料流和控制流分析,确定程式語義是合法的、符合邏輯的。
- 符号引用驗證:確定解析動作能正确執行。
驗證階段是非常重要的,但不是必須的,它對程式運作期沒有影響,如果所引用的類經過反複驗證,那麼可以考慮采用
-Xverifynone
參數來關閉大部分的類驗證措施,以縮短虛拟機類加載的時間。
準備:為類的靜态變量配置設定記憶體,并将其初始化為預設值
準備階段是正式為類變量配置設定記憶體并設定類變量初始值的階段,這些變量所使用的記憶體都将在方法區中進行配置設定。
該階段的注意事項:
- 這時候進行記憶體配置設定的僅包括類變量(被static修飾的變量),而不包括執行個體變量,執行個體變量将會在對象執行個體化時随着對象一起配置設定在Java堆中。
- 這裡所設定的初始值通常情況下是資料類型預設的零值(如 、
、0L
、null
等),而不是被在Java代碼中被顯式地賦予的值。false
比如:假設一個類變量的定義為:
public static int value = 3
;那麼變量value在準備階段過後的初始值為
,而不是
3
,因為這時候尚未開始執行任何Java方法,而把value指派為3的
put static
指令是在程式編譯後,存放于類構造器
()
方法之中的,是以把value指派為3的動作将在初始化階段才會執行。
- 對基本資料類型來說,對于類變量(static)和全局變量,如果不顯式地對其指派而直接使用,則系統會為其賦予預設的零值,而對于局部變量來說,在使用前必須顯式地為其指派,否則編譯時不通過。
- 對于同時被
和static
修飾的常量,必須在聲明的時候就為其顯式地指派,否則編譯時不通過;而隻被final修飾的常量則既可以在聲明時顯式地為其指派,也可以在類初始化時顯式地為其指派,總之,在使用前必須為其顯式地指派,系統不會為其賦予預設零值。final
- 對于引用資料類型
來說,如數組引用、對象引用等,如果沒有對其進行顯式地指派而直接使用,系統都會為其賦予預設的零值,即reference
。null
- 如果在數組初始化時沒有對數組中的各元素指派,那麼其中的元素将根據對應的資料類型而被賦予預設的零值。
- 如果類字段的字段屬性表中存在ConstantValue屬性,即同時被final和static修飾,那麼在準備階段變量value就會被初始化為ConstValue屬性所指定的值。假設上面的類變量value被定義為:
編譯時Javac将會為value生成ConstantValue屬性,在準備階段虛拟機就會根據ConstantValue的設定将value指派為3。我們可以了解為public static final int value = 3;
常量在編譯期就将其結果放入了調用它的類的常量池中static final
解析:把類中的符号引用轉換為直接引用
解析階段是虛拟機将常量池内的符号引用替換為直接引用的過程,解析動作主要針對
類
或
接口
、
字段
、
類方法
、
接口方法
、
方法類型
、
方法句柄
和
調用點
限定符7類符号引用進行。符号引用就是一組符号來描述目标,可以是任何字面量。
直接引用
就是直接指向目标的指針、相對偏移量或一個間接定位到目标的句柄。
初始化:對類的靜态變量,靜态代碼塊執行初始化操作
初始化,為類的靜态變量賦予正确的初始值,JVM負責對類進行初始化,主要對類變量進行初始化。在Java中對類變量進行初始值設定有兩種方式:
- 聲明類變量是指定初始值
- 使用靜态代碼塊為類變量指定初始值
類初始化的步驟
- 假如這個類還沒有被加載和連接配接,則程式先加載并連接配接該類
- 假如該類的直接父類還沒有被初始化,則先初始化其直接父類
- 假如類中有初始化語句,則系統依次執行這些初始化語句
觸發類初始化的時機
隻有當對類的主動使用的時候才會導緻類的初始化,類的主動使用包括以下六種:
- 使用new關鍵字執行個體化對象的時候。
- 讀取或設定一個類型的靜态字段(被final修飾、已在編譯期把結果放入常量池的靜态字段除外)的時候。
- 調用一個類型的靜态方法的時候。
- 使用java.lang.reflect包的方法對類型進行反射調用的時候,如果類型沒有進行過初始化,則需要先觸發其初始化。
- 當初始化類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
- 當虛拟機啟動時,使用者需要指定一個要執行的主類(包含main()方法的那個類),虛拟機會先初始化這個主類。
以下幾種情況不會執行類初始化
- 通過子類引用父類的靜态字段,隻會觸發父類的初始化,而不會觸發子類的初始化。
- 定義對象數組,不會觸發該類的初始化。
- 常量在編譯期間會存入調用類的常量池中,本質上并沒有直接引用定義常量的類,不會觸 發定義常量所在的類。
- 通過類名擷取 Class 對象,不會觸發類的初始化。
- 通過 Class.forName 加載指定類時,如果指定參數 initialize 為 false 時,也不會觸發類初 始化,其實這個參數是告訴虛拟機,是否要對類進行初始化。
- 通過 ClassLoader 預設的 loadClass 方法,也不會觸發初始化動作。
使用
類通路方法區内的資料結構的接口, 對象是Heap區的資料。
解除安裝
Java虛拟機将結束生命周期的幾種情況
- 執行了System.exit()方法
- 程式正常執行結束
- 程式在執行過程中遇到了異常或錯誤而異常終止
- 由于作業系統出現錯誤而導緻Java虛拟機程序終止
類加載器
什麼是類加載器
虛拟機設計團隊把類加載階段中的“通過一個類的全限定名來擷取描述此類的二進制位元組流”這個動作放到Java虛拟機外部去實作,以便讓應用程式自己決定如何去擷取所需要的類。 實作這個動作的代碼子產品稱為“類加載器”。
類加載器的層次
雙親委派模型要求除了頂層的啟動類加載器外,其餘的類加載器都應有自己的父類加載器。不過這裡類加載器之間的父子關系一般不是以繼承(Inheritance)的關系來實作的,而是通常使用組合(Composition)關系來複用父加載器的代碼。
從Java虛拟機的角度來講,隻存在兩種不同的類加載器:一種是啟動類加載器(Bootstrap ClassLoader),這個類加載器使用C++語言實作,是虛拟機自身的一部分;另一種就是所有其他的類加載器,這些類加載器都由Java語言實作,獨立于虛拟機外部,并且全都繼承自抽象類java.lang.ClassLoader。
從Java開發人員的角度來看,類加載器還可以劃分得更細緻一些,絕大部分Java程式都會使用到以下3種系統提供的類加載器:
啟動類加載器(Bootstrap ClassLoader)
這個類将器負責将存放在<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數所指定的路徑中的,并且是虛拟機識别的(按照檔案名識别,如rt.jar、tools.jar,名字不符合的類庫即使放在lib目錄中也不會被加載)類庫加載到虛拟機記憶體中。
擴充類加載器(Extension ClassLoader)
這個加載器由sun.misc.Launcher$ExtClassLoader實作,它負責加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴充類加載器。
應用程式類加載器(Application ClassLoader)
這個類加載器由sun.misc.Launcher$AppClassLoader來實作。由于應用程式類加載器是ClassLoader類中的getSystem-ClassLoader()方法的傳回值,是以有些場合中也稱它為“系統類加載器”。
它負責加載使用者類路徑(ClassPath)上所有的類庫,開發者同樣可以直接在代碼中使用這個類加載器。如果應用程式中沒有自定義過自己的類加載器,一般情況下這個就是程式中預設的類加載器。
我們的應用程式都是由這3種類加載器互相配合進行加載的,如果有必要,還可以加入自己定義的類加載器。
那麼如何才能正确的掌握Redis呢?
為了讓大家能夠在Redis上能夠加深,是以這次給大家準備了一些Redis的學習資料,還有一些大廠的面試題,包括以下這些面試題
- 并發程式設計面試題彙總
- JVM面試題彙總
- Netty常被問到的那些面試題彙總
- Tomcat面試題整理彙總
- Mysql面試題彙總
- Spring源碼深度解析
- Mybatis常見面試題彙總
- Nginx那些面試題彙總
- Zookeeper面試題彙總
- RabbitMQ常見面試題彙總
JVM常頻面試:
Mysql面試題彙總(一)
Mysql面試題彙總(二)
Redis常見面試題彙總(300+題)
有需要的朋友,可以直接點選這裡免費擷取
Mysql面試題彙總(二)
[外鍊圖檔轉存中…(img-dD68Y3Gg-1627038163392)]
Redis常見面試題彙總(300+題)
[外鍊圖檔轉存中…(img-Owog2u7S-1627038163393)]
有需要的朋友,可以直接點選這裡免費擷取
絕無套路!!