1. 程式人生 > >關於Jvm類載入機制,這一篇就夠了

關於Jvm類載入機制,這一篇就夠了

前言

一個月沒更新了,這個月發生了太多的事情,導致更新的頻率大大降低,不管怎樣收拾心情,技術的研究不能落下!

jvm作為每個java程式猿必須瞭解的知識,博主推薦一本書《深入理解Java虛擬機器》,以前博主在學校的時候看過幾遍,每一次看都有新的理解。加上工作了也有一年多的時間了,有必要好好總結一番~

什麼是jvm

平常我們編寫程式碼都是編寫的.java檔案,怎麼部署到機器上執行呢?通過打jar包或者war包,然後部署執行。

如果看過jar包的內容那麼就能知道,我們寫的.java檔案全部被編譯成了.class檔案。

這裡發生了很重要的一個步驟——編譯:將我們寫的程式翻譯成能被jvm讀懂的檔案格式。

值得注意的是,每一個類都會被編譯成一個.class檔案,包括內部類等。也就是說每一個.class檔案都只對應我們程式碼中的一個類。

類的生命週期

類被載入到jvm虛擬機器記憶體開始,到卸載出記憶體為止,他的生命週期可以分為:載入->驗證->準備->解析->初始化->使用->解除安裝。

下面我們來對此一一說明:

載入

當生成一個jar包以後,我們編寫的程式就全部編編譯成了jvm能讀懂的.class格式。此時就需要載入了,將我們的編譯好的.class檔案載入到jvm中。此時就會有一個“類載入器”的概念。如下圖。

接下來一個問題,類載入器何時會將一個.class載入帶jvm?也就是說什麼情況下會載入一個類?

一個jar包執行的時候會指定一個main()方法作為入口方法。首先就會將main()方法所在的類載入到jvm,當代碼執行遇到new的時候又繼續將該物件載入到jvm。

所以總結來說,就是在你的程式碼中需要用到這個類的時候,就會將其載入到jvm中。

驗證

這個不需要理解的太深,很直白的道理,不能什麼阿貓阿狗都能被載入到jvm中,要不就亂套了。所以該階段就是來校驗載入進來的.class檔案是否符合指定的規則。

有一個很有趣的就是,每個.class檔案都很浪漫,因為每一個.class檔案都是以8個十六進位制的 0×CAFEBABE,翻譯過來就是咖啡寶貝。浪漫吧?在驗證階段的第一步就是檢查.class檔案是否以咖啡寶貝來開頭的。

所以我們的流程圖可以更新為

準備

當我們合法的把一個.class檔案載入到jvm中後,此時就會進行一些準備工作。

首先為這個類分配記憶體空間,然後為類變數(被static修飾的變數)賦值一個預設的初始值。但是如果類變數同時被final修飾的話,就不是賦值初始值而是具體的值

用下面兩種情況來說明:

public class Student{
    private static int age = 18;
}
//此時就會為age變數分配記憶體空間並且為其賦值 0 這個初始值。
public class Student{
    private static final int age = 18;
}
//age被final修飾,此時就會為age變數分配記憶體空間並且為其賦值為 18 。

所以我們的流程圖可以更新為

解析

解析階段就是jvm將常量池的符號引用替換為直接引用。

簡單的來說就是我們編寫的程式碼中,當一個變數引用某個物件的時候,這個引用在.class檔案中是以符號引用來儲存的。在解析階段就需要將其解析為直接引用。如果有了直接引用,那引用的目標必定已經在記憶體中存在。

所以我們的流程圖可以更新為

初始化

在準備階段我們已經為載入到jvm的類分配了記憶體空間並且為類變數賦予了初始值。

而到了初始化階段,才真正開始執行類中定義的java程式程式碼。主要有以下步驟:

  1. 為類的靜態變數賦予正確的初始值。
  2. 執行類的靜態程式碼塊。

按照順序自上而下執行類中的變數賦值語句和靜態語句,並且只有類或介面被Java程式首次主動使用時才初始化他們。如果有父類,則首先按照順序執行父類中的變數賦值語句和靜態語句。

所以我們的流程圖可以更新為

總結

在一個靜態方法中我們是不能直接使用非靜態變數的。當我們使用靜態方法的時候,僅僅是初始化了靜態方法所在的類,此時只有靜態變數是被賦了值而非靜態變數是沒有被賦值的。所以在靜態方法中是不能直接使用非靜態變數的。這是我的理解,如果理解有誤,歡迎私信博主或留言