1. 程式人生 > >聊聊JVM——類的載入(一)

聊聊JVM——類的載入(一)

前言:

        最近在看一本《深入理解java虛擬機器》的書,在學類載入機制和結合眾多大佬的表達並結合自己的理解寫下這一篇部落格,記錄自己的困惑,以防以後忘記。

閱讀須知:

        此次部落格以啟發性程式碼和解釋進行學習。在閱讀時,按照程式碼和提示進行思考分析為什麼,請思考過後在看答案來驗證自己的思考。(此部落格為個人觀點且本人水平有限,如有錯,請批評指正

小題測試:(先思考,並寫出自己的答案後在往下看

package com.wen.demo.test.MyClass;

class Singlen{
    private static Singlen singlen= new Singlen();
    static int count1;
    static int count2=0;


    public Singlen(){
        count1++;
        count2++;
    }

    public static Singlen getInstance(){
        return Singlen.singlen;
    }
}
public class MyClass {

    public static void main(String[] args) {

        Singlen singlen= Singlen.getInstance();
        System.out.println("count1的值為:"+Singlen.count1);
        System.out.println("count2的值為:"+Singlen.count2);
    }
}

                                              對於以上程式碼,請先思考,輸出的是什麼。然後才往下面看。

                                                                                       。

                                                                                       。

                                                                                       。

                                                                                       。

概念:

         我們在學java時都有接觸過,執行java程式之前,會將我們寫的*.java檔案編譯成.class檔案。但是好像我們在學的時候,老師或許就講了這個,然後就沒然後了。

          我們先看看這個張圖:

          基本上是分為這7份階段,此次只講講前5個階段。其中驗證——準備——解析通常稱為一個連線階段

載入階段:

         在此階段,虛擬機器需要完成三件事情:

         (1)通過一個類的全限定名來獲取定義此類的二進位制位元組流

         (2)將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構

         (3)在記憶體中生成代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的訪問入口

                                                          (以上來自《深入理解java虛擬機器》書上內容)

          上面的解釋已經很清楚了,如果你還是不能理解,先看接下來這個 簡化版:

          在載入的時候,虛擬機器查詢我們寫的.java檔案,編譯成位元組碼後加載到記憶體裡,然後在記憶體中生成一個java.lang.Class物件,作為方法區這個類的各種資料的訪問入口。

           嗯,好像還是有點長,在簡單點就是查詢並載入類的二進位制資料到記憶體中。

驗證階段:

          驗證是連線階段的第一步,他主要是確保Class檔案的位元組流中的資訊符合虛擬機器的要求,並且不會危害虛擬機器的自身的安全。為什麼要驗證,不都是我們寫的編譯來的嗎?假設虛擬機器不驗證位元組流檔案,那樣放一些惡意惡意的位元組流,那感覺會“灰常”開心些吧!~

準備階段:

            這一階段,主要是為類的靜態變數分配記憶體,並將其初始化為預設值。例如:

           public static int value =123;

            上面這個靜態變數,在準備階段:

                         將value值初始化為0而不是123

                         將value值初始化為0而不是123

                         將value值初始化為0而不是123

             當然,這是通常情況下,想想女生每個月總有那麼幾天,所以我們java虛擬機器也要有那麼幾天的特殊情況嘛,。怎麼特殊呢?

         public static final int value =123;

            加一個final關鍵字後,這個value就是個常量了。常量在編譯階段就會存入呼叫類的常量池中,所以在這個準備階段時,java虛擬機器就會將value賦值了。

解析階段:

          解析階段是指虛擬機器將常量池中的符號引用替換為直接引用的過程(這裡我也不是特別懂,太多東西了,看的腦闊疼)想要深入瞭解請去閱讀《深入理解java虛擬機器》第7章220頁

初始化階段:

        這一步是類載入過程中的最後一步了,前面的類載入中除了使用者應用程式可用通過自定義類載入器參與外,其他都是有JVM主導和控制的,到這一個階段才是真正懂得執行類中定義的java程式程式碼。

       比如:我們在準備階段的時候,已經為變數初始化了一次(此初始化不是現在這個初始化)這時,虛擬機器要為類的靜態變數賦予的初始化值(就是我們寫的程式碼定義的值value=123啦

                      注:此次不涉及類的初始化,那樣介紹太多了。持續懶惰中~!

                                                                                       。

                                                                                       。

                                                                                       。

                                                                                       。

      看完上面的,在想想自己的之前寫的答案,下面我們將公佈答案:

                                                    

      Why?懷疑人生了?還是高興我作對了。如果做錯的同學,請再看一次5個階段理解一下。

      流程簡單解析:  

           我們在呼叫時單例時new 例項屬於主動使用(首次使用才有效,具體下一篇部落格介紹)。這時類開始初始化,類的載入順序是從上往下的。初始化了Singlen類,count1和count2都已經加了1是吧~!類的載入順序是從上往下的,這時count1是1,count2也是1。接下來走count1,再走count2了。

             由於count1沒有被賦值,可以理解為我們程式猿給他賦了值:

    static int count1  = 1;

             count2被我們自己賦值過了是不是?那麼虛擬機器在會將0賦值給count2。這時count2就是0了。所以結果就是1和0咯

總結:

         沒想到,這篇博文我會寫了快3小時了。但是,也是收穫滿滿滴。java虛擬機器部分是基礎,對於我們來說是必須去學的,只有懂了,我們才會學會去優化,才能走的更遠。謝謝大家,注大家學習進步,工作愉快~!

程式人生,與君共勉~!