Java常量池解析與字串intern簡介

來源:互聯網
上載者:User

Java常量池解析與字串intern簡介

在Java應用程式運行時,Java虛擬機器會儲存一份內部的運行時常量池,它區別於class檔案的常量池,是class檔案常量池映射到虛擬機器中的資料結構。 關於class檔案常量池的部分可以參考之前的博文執行個體探索Class檔案。

1.CONSTANT_Class入口解析

數組類的符號解析較為特殊。若是基本類型數組,那麼虛擬機器將建立該基本類型的新數組類,並建立一個Class執行個體來代表該類型,數組類的定義類載入 器為 啟動類載入器。若是參考型別的數組,那麼在此之前還會進行參考型別的解析,數組類的定義類載入器為參考型別的定義類載入器。

非數組類和介面的的解析將經曆以下步驟:

(1).載入該類型和其所有的超類型

如果該類型在此之前已經裝載到了虛擬機器的當前命名空間,那麼直接使用已經被裝載的類型即可,否則由引用的發起類的初始類載入器進行載入。對目標類型 的超類的載入必然是在對當前類型載入完的基礎上進行的,因為只有載入完當前類型,才能從class檔案的super_class域找到其直接超類的符號引 用,再遞迴進行解析和載入,直至java.lang.Object類。而在遞迴返回的過程中,會檢查interfaces域以查看實現或擴充了哪些介面, 並再次遞迴遍曆對介面的符號引用。

(2).檢查存取權限

隨後是對目標類型的串連和初始化,這樣才可以正常使用該類型。前面提到,對目標類型的初始化需要其所有超類都必須進行初始化(超介面不是必須的), 並且,由於已經對其超類進行了載入,所以不必再依賴於自該類向Object類的解析順序,而是從Object類向該類進行初始化。類型的串連和初始化步驟 如下:

(3).類型校正

(4).類型準備

(5).類型解析(可延遲)

注意該過程是對被參考型別及其超類的符號引用的解析,因為對於被參考型別的某些符號引用不會立刻用到,故該步驟之前是嚴格意義上屬於發起引用的類型 的符號 解析的過程。只有在主動使用被參考型別的這些符號引用所指向的類型時,才會對這些符號引用進行解析,對其所指向的類型進行裝載、串連和初始化。

(6).類型初始化

2.CONSTANT_Fieldref入口解析

由於一個類型不會含有其超類型所定義的欄位,所以對目標欄位的搜尋將會從欄位所 指向的類型開始,從該類型開始搜尋,再遞迴搜尋其所實現或擴充的介面,再遞迴搜尋其超類,直至找到目標欄位,並會將運行時常量池的該欄位入口標記為已解 析,並在該常量池的資料上改為對這個欄位的直接引用。

3.CONSTANT_Methodref入口解析

與欄位的搜尋類似但有所不同,其搜尋順序將從該類型開始,再遞迴搜尋其超類,在遞迴搜尋其所實現或擴充的介面。

4.CONSTANT_InterfaceMethodRef入口解析

對介面方法的搜尋就是從被解析的介面開始,向其超介面遞迴搜尋。

5.CONSTANT_String入口解析

Java虛擬機器會將字串處理為一個字串對象加以維護,而虛擬機器所維護的就是一張 字串池,它包含所有被”拘留”的字串對象的引用。對CONSTANT_String常量池的解析首先就要查看字串池中該字串對象的引用是否存在, 如果存在則直接把常量池資料解析為該字串對象的引用,若不存在,那麼就需要根據這個字串序列建立一個字串對象,並將其引用加入到字串池中,並將常 量池資料解析為該引用。

也可以使用String對象的intern對象來拘留一個字串(注意並非字串對象),若該字串池中存在對該字 符串序列的對象的引用,那麼直接返回該引用即可,否則,將會拘留該字串,但注意拘留返回的字串對象引用將不會指向原String對象,因為原 String對象位於Java堆,而字串池的對象是虛擬機器所建立的,由虛擬機器所維護。

 
  1. package com.ice.intern; 
  2.  
  3. public class InternTest { 
  4.  
  5.     public static void main(String args[]){ 
  6.         String a = new String("123"); 
  7.         String b = a; 
  8.         String c = new String("123");; 
  9.  
  10.         System.out.println("before intern:"); 
  11.         System.out.println("a = b ? :" + (a == b)); 
  12.         System.out.println("a = c ? :" + (a == c)); 
  13.  
  14.         a = a.intern(); 
  15.         c = c.intern(); 
  16.  
  17.         System.out.println("after intern:"); 
  18.         System.out.println("a = b ? :" + (a == b)); 
  19.         System.out.println("a = c ? :" + (a == c)); 
  20.     } 
  21.  

結果如下:

(6).其他類型(資料基本類型)入口解析

直接使用常量池所包含的常量值即可

6.直接引用

常量池解析最終將符號引用替換成為直接引用。指向類型、類變數和類方法的直接引用可能為在方法區的指標。而指向執行個體變數和執行個體方法的直接引用是從對象映像的開始到該執行個體變數或方法表的位移。

執行個體變數的組織方式為:從Object類開始到該執行個體的類型,將類中聲明的執行個體變數按在class檔案中出現的順序依次放在對象映像中。
執行個體方法的組織方式較為類似:從Object類開始到該執行個體的類型,將類中聲明的執行個體方法指標按在class檔案中出現的順序依次放在對象映像中。但對於重寫的方法將出現在超類對應的位置(該方法第一次出現的位置)。

但是提供者方法就不能簡單地通過方法表的位移量來進行訪問,而必須搜尋對象的類的方法表來找到該方法。

比如Factory介面分別由A和B來實現其produce()方法,但由於A和B不能保證由同一個實現了Factory介面的超類派生,即有著同樣的produce()方法位移,那麼就無法通過方法表的位移來訪問Factory的produce()方法。

7.裝載約束

對於一個類型指向另一個類型的符號引用,如果引用的類型和被參考型別並非由同一個初始載入器載入(可能通過使用者自定 義ClassLoader來實現),那麼虛擬機器就必須確保被參考型別在不同的命名空間中保持一致。這樣就通過自訂ClassLoader來載入不受信類 型後,就不會發生解析對被參考型別的符號引用時,把受信的類型當做已經被解析過的不受信類型(因為對方法的符號引用只有許可權定名和描述符,並不會也無法得 知其初始類載入器),從而調用了不受信類型的方法訪問受信類型的受保護的成員。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.