JVM內部對String類型的處理分析

首先我們使用例子的形式看看JVM是如何處理String類型的?

1、簡單的例子

a. String s1=new String("Hello");

b. String s2="Hello";

c. Object obj=new Object();

 

2、生成的字節碼

JVM將上面的程序編譯後生成的字節碼如下:

a.     0:  new                      #2;    //class java/lang/String

       3:  dup  

       4:  ldc                      #3;    //String Hello

       6:  invokeespecial           #4;    //Method   java/lang/String."<init>":(Ljava/lang/String;) V

       9:  astore_1

b.     10: ldc                      #3;    //String Hello

       12:astore_2

c.     13:new                       #5;    //class java/lang/object

       16:dup

       17:invokespecial             #1;    //Method  java/lang/Object."<init>":()V

       20:astore_3

3、指令解析

new 指令格式:  new indexbyte1,indexbyte2

執行過程:

要執行new指令,JVM通過計算(indexbyte1<<8)|indexbyte2生成一個指向常量池的無符號的16位索引,然後根據這個索引查找JVM常量池的入口。該索引所指向的常量池入口必須爲CONSTANT_Class_info。CONSTANT_Class_info有一個tag和一個指向常量池的索引所組成。如果該入口不存在,那麼JVM將解析這個常量池入口,該入口類型必須爲類。JVM堆中爲新的對象映像分配足夠大的空間,並將對象的實例變量設置爲默認值。最後JVM將指向新的對象的引用objectref壓入操作數棧。

 

dup 指令格式:  dup

執行過程:

要執行dup指令,JVM複製了操作數棧頂部一個字長的內容,然後再將複製內容壓入棧。本指令能夠從操作數棧頂部複製任何單位字長的值。但絕對 不要使用它來複制操作數棧頂部任何兩個字長(long型或double型)中的一個字長。上面例中,即複製引用objectref,這時在操作數棧存在2 個引用。

 

ldc 指令格式:  ldc,index

執行過程:

要執行ldc指令,JVM首先查找index所指定的常量池入口,在index指向的JVM常量池入口,JVM將會查找 CONSTANT_Integer_info,CONSTANT_Float_info和CONSTANT_String_info入口。如果還沒有這些 入口,JVM會解析它們。而對於上面的haha,JVM會找到CONSTANT_String_info入口,同時,將把指向被拘留String對象(由 解析該入口的進程產生)的引用壓入操作數棧。

 

invokespecial 指令格式:  invokespecial indextype1,indextype2

執行過程:
對於該類而言,該指令是用來進行實例初始化方法的調用。上面例子中,即通過其中一個引用調用String類的構造器,初始化對象實例,讓另一個相同的引用 指向這個被初始化的對象實例,然後前一個引用彈出操作數棧。

 

astore_1指令格式:   astore_1

astore_1指令過程:
要執行astore_1指令,JVM從操作數棧頂部彈出一個引用類型或者returnAddress類型值,然後將該值存入由索引1指定的局部變量中,即 將引用類型或者returnAddress類型值存入局部變量1。

 

String 類型在編碼中字面上的對象,是會進行查詢String 常量池表的(如果沒有就創建一個String 對象,然後再保存在表中)。而new String()生成的對象是不會查詢String常量池表的,它不管表中是否存在,都會創建一個新的String 對象,所以內存地址是肯定不同的。而如果調用String 方法中的intern()方法,則會在String常量池表中查找,然後把該表中的引用返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章