運行時棧幀結構


棧幀(Stack Frame) 是用於虛擬機執行時方法調用和方法執行時的數據結構,它是虛擬棧數據區的組成元素。每一個方法從調用到方法返回都對應着一個棧幀入棧出棧的過程。

每一個棧幀在編譯程序代碼的時候所需要多大的局部變量表,多深的操作數棧都已經決定了,並且寫入到方發表的 Code 屬性之中,一次一個棧幀需要多少內存,不會受到程序運行期變量數據的影響,僅僅取決於具體的虛擬機實現。

典型的棧幀主要由 局部變量表(Local Stack Frame)、操作數棧(Operand Stack)、動態鏈接(Dynamic Linking)、返回地址(Return Address)組成,如下圖所示:

 

    public static void main(String[] args) {
        String s1 = "Hello";
        String s2 = "Hello";
     //s3雖然是動態拼接出來的字符串,但是所有參與拼接的部分都是已知的字面量,在編譯期間,這種拼接會被優化,編譯器直接幫你拼好,
String s3 = "Hel" + "lo"; //Hel被包裝為對象,lo被包裝為對象, //然后調用 StringBuilder.append方法, //然后調用String.toStringui方法, //存入變量s4 String s4 = "Hel" + new String("lo"); //調用new 生成新對象 String s5 = new String("Hello"); String s6 = s5.intern(); String s7 = "H"; String s8 = "ello"; String s9 = s7 + s8; System.out.println(s1 == s2); // true System.out.println(s1 == s3); // true System.out.println(s1 == s4); // false System.out.println(s1 == s9); // false System.out.println(s4 == s5); // false System.out.println(s1 == s6); // true }

其對應指令集如下: 

         0: ldc           #5                  // String Hello
         2: astore_1
         3: ldc           #5                  // String Hello
         5: astore_2
         6: ldc           #5                  // String Hello
         8: astore_3
         9: new           #6                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        16: ldc           #8                  // String Hel
        18: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: new           #10                 // class java/lang/String
        24: dup
        25: ldc           #11                 // String lo
        27: invokespecial #12                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        30: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        33: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        36: astore        4
        38: new           #10                 // class java/lang/String
        41: dup
        42: ldc           #5                  // String Hello
        44: invokespecial #12                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        47: astore        5
        49: aload         5
        51: invokevirtual #14                 // Method java/lang/String.intern:()Ljava/lang/String;
        54: astore        6
        56: ldc           #15                 // String H
        58: astore        7
        60: ldc           #16                 // String ello
        62: astore        8
        64: new           #6                  // class java/lang/StringBuilder
        67: dup
        68: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        71: aload         7
        73: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        76: aload         8
        78: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        81: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        84: astore        9

 


 

常量池:

Java中的常量池,實際上分為兩種形態:靜態常量池運行時常量池

     所謂靜態常量池,即*.class文件中的常量池,class文件中的常量池不僅僅包含字符串(數字)字面量,還包含類、方法的信息,占用class文件絕大部分空間。

  這種常量池主要用於存放兩大類常量:字面量(Literal)和符號引用量(Symbolic References),

  字面量相當於Java語言層面常量的概念,如文本字符串,聲明為final的常量值等,

 符號引用則屬於編譯原理方面的概念,包括了如下三種類型的常量:

  • 類和接口的全限定名
  • 字段名稱和描述符
  • 方法名稱和描述符

     而運行時常量池,則是jvm虛擬機在完成類裝載操作后,將class文件中的常量池載入到內存中,並保存在方法區中,我們常說的常量池,就是指方法區中的運行時常量池。

運行時常量池相對於CLass文件常量池的另外一個重要特征是 具備動態性,Java語言並不要求常量一定只有編譯期才能產生,也就是並非預置入CLass文件中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用比較多的就是 String類的intern()方法。
String的intern()方法會查找在常量池中是否存在一份equal相等的字符串,如果有則返回該字符串的引用,如果沒有則添加自己的字符串進入常量池。
 
      常量池的好處
常量池是為了避免頻繁的創建和銷毀對象而影響系統性能,其實現了對象的共享。
例如字符串常量池,在編譯階段就把所有的字符串文字放到一個常量池中。
(1)節省內存空間:常量池中所有相同的字符串常量被合並,只占用一個空間。
(2)節省運行時間:比較字符串時,==比equals()快。對於兩個引用變量,只用==判斷引用是否相等,也就可以判斷實際值是否相等。
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM