JVM生命周期之類的加載、連接、初始化

jopen 10年前發布 | 11K 次閱讀 JVM Java開發

加載

       在java中,我們經常會接觸到一個詞——類加載,它和這里的加載并不是一回事,通常我們說類加 載指的是類的生命周期中加載、連接、初始化三個階段。在加載階段,java虛擬機會做什么工作呢?其實很簡單,就是找到需要加載的類并把類的信息加載到 jvm的方法區中,然后在堆區中實例化一個java.lang.Class對象,作為方法區中這個類的信息的入口。

       類的加載方式比較靈活,我們最常用的加載方式有兩種,一種是根據類的全路徑名找到相應的class文件,然后從class文件中讀取文件內容;另一種是從jar文件中讀取。另外,還有下面幾種方式也比較常用:

  • 從網絡中獲取:比如10年前十分流行的Applet。

    </li>

  • 根據一定的規則實時生成,比如設計模式中的動態代理模式,就是根據相應的類自動生成它的代理類。

    </li>

  • 從非class文件中獲取,其實這與直接從class文件中獲取的方式本質上是一樣的,這些非class文件在jvm中運行之前會被轉換為可被jvm所識別的字節碼文件。

    </li> </ul>

           對于加載的時機,各個虛擬機的做法并不一樣,但是有一個原則,就是當jvm“預期”到一個類將要被使用時,就會在使用它之前對這個類進行加載。比如說,在 一段代碼中出現了一個類的名字,jvm在執行這段代碼之前并不能確定這個類是否會被使用到,于是,有些jvm會在執行前就加載這個類,而有些則在真正需要 用的時候才會去加載它,這取決于具體的jvm實現。我們常用的hotspot虛擬機是采用的后者,就是說當真正用到一個類的時候才對它進行加載。

           加載階段是類的生命周期中的第一個階段,加載階段之后,是連接階段。有一點需要注意,就是有時連接階段并不會等加載階段完全完成之后才開始,而是交叉進 行,可能一個類只加載了一部分之后,連接階段就已經開始了。但是這兩個階段總的開始時間和完成時間總是固定的:加載階段總是在連接階段之前開始,連接階段 總是在加載階段完成之后完成。

     

    連接

           連接階段比較復雜,一般會跟加載階段和初始化階段交叉進行,這個階段的主要任務就是做一些加載后的驗證工作以及一些初始化前的準備工作,可以細分為三個步驟:驗證、準備和解析。

    1. 驗證:當一個類被加載之后,必須要驗證一下這個類是否合法,比如這個類是不是符合字節碼的格式、變量與方法是不是有重復、數據類型是不是有效、繼承與實現是否合乎標準等等。總之,這個階段的目的就是保證加載的類是能夠被jvm所運行。

      </li>

    2. 準備:準備階段的工作就是為類的靜態變量分配內存并設為jvm默認的初值,對于非靜態的變量,則不會為它們分配內存。有一點需要注意,這時候,靜態變量的初值為jvm默認的初值,而不是我們在程序中設定的初值。jvm默認的初值是這樣的:

      </li>

      • 基本類型(int、long、short、char、byte、boolean、float、double)的默認值為0。

        </li>

      • 引用類型的默認值為null。

        </li>

      • 常量的默認值為我們程序中設定的值,比如我們在程序中定義final static int a = 100,則準備階段中a的初值就是100。

        </li> </ul>

      •  解析:這一階段的任務就是把常量池中的符號引用轉換為直接引用。那么什么是符號引用,什么又是直 接引用呢?我們來舉個例子:我們要找一個人,我們現有的信息是這個人的身份證號是1234567890。只有這個信息我們顯然找不到這個人,但是通過公安 局的身份系統,我們輸入1234567890這個號之后,就會得到它的全部信息:比如安徽省黃山市余暇村18號張三,通過這個信息我們就能找到這個人了。 這里,123456790就好比是一個符號引用,而安徽省黃山市余暇村18號張三就是直接引用。在內存中也是一樣,比如我們要在內存中找一個類里面的一個 叫做show的方法,顯然是找不到。但是在解析階段,jvm就會把show這個名字轉換為指向方法區的的一塊內存地址,比如c17164,通過 c17164就可以找到show這個方法具體分配在內存的哪一個區域了。這里show就是符號引用,而c17164就是直接引用。在解析階段,jvm會將 所有的類或接口名、字段名、方法名轉換為具體的內存地址。

        </li> </ol>

                連接階段完成之后會根據使用的情況(直接引用還是被動引用)來選擇是否對類進行初始化。

         

        初始化

               如果一個類被直接引用,就會觸發類的初始化。在java中,直接引用的情況有:

        • 通過new關鍵字實例化對象、讀取或設置類的靜態變量、調用類的靜態方法。

          </li>

        • 通過反射方式執行以上三種行為。

          </li>

        • 初始化子類的時候,會觸發父類的初始化。

          </li>

        • 作為程序入口直接運行時(也就是直接調用main方法)。

          </li> </ul>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!