【JVM虛擬機】(5)---深入理解JVM-Class中常量池


深入理解Class---常量池

一、概念

1、jvm生命周期

啟動:當啟動一個java程序時,一個jvm實例就誕生了,任何一個擁有main方法的class都可以作為jvm實例運行的起點。

運行:main()函數作為程序初始線程起點,其它線程由該線程啟動,包括守護線程(daemon)和non-daemon(普通線程)。守護線程是JVM自己使用的線程比如GC線程就是個守護線程,只要這個jvm實例還有普通線程執行,就不會停止,但是可以用exit()強制終止程序。

消亡:所有非守護線程退出時,JVM實例結束生命,若安全管理器允許,程序也可以使用java.lang.Runtime類或者System.exit(0)來退出。實際上exit也是用到Runtime類來退出,Runtime是個神奇的類,它還可以用於啟動和關閉非java進程。

2、JVM與Class文件

我們一直說java虛擬機實現的與語言是無關的,java虛擬機不和包含java在內的任何語言綁定,它只和與class文件這種特殊的二進制文件格式所關聯,class文件中包含了java虛擬機指令集符號表以及若干其他輔助信息。基於安全方面的考慮, Java 慮擬機規范要求在 Class 文件中使用許多強制性的語法和結構化約束,但任一門功能性語言都可以表示為一個能被 Java 虛擬機所接受的有效的 Class 文件。作為一個通用的、機器無關的執行平台,任何其他語言的實現者都可以將 Java 虛擬機作為語言的產品交付媒介。例如,使用 Java 編譯器可以把 Java 代碼編譯為存儲字節碼的 Class 文件,使用 JRuby 等其他語言的編譯器同樣可以把程序代碼編譯成 Class 文件,虛擬機並不關心Class 的來源是何種語言,如圖。

3、什么是Class文件

Java字節碼類文件(.class)是Java編譯器編譯Java源文件(.java)產生的“目標文件”。它是一種8位字節的二進制流文件, 各個數據項按順序緊密的從前向后排列, 相鄰的項之間沒有間隙, 這樣可以使得class文件非常緊湊, 體積輕巧, 可以被JVM快速的加載至內存, 並且占據較少的內存空間(方便於網絡的傳輸)。

class文件是一組以8位字節為基礎單位的二進制流。

class文件中的信息是一項一項排列的, 每項數據都有它的固定長度, 有的占一個字節, 有的占兩個字節, 還有的占四個字節或8個字節, 數據項的不同長度分別用u1, u2, u4, u8表示, 分別表示一種數據項在class文件中占據一個字節, 兩個字節, 4個字節和8個字節。

4、什么是魔數

當我們把class文件轉成16進制,我們可以看到文件的頭四個字節是cafe babe,這個就稱為魔數。,它唯一作用就告訴虛擬機當前的文件就是class文件。 使用魔數而不是用擴展名來進行識別主要是基於安全考慮,因為擴展名我們可以隨意通過重命名等方式改動。而通過魔數就算你把結尾改成.clss。但它同樣還能在JVM運行,因為它的頭部還是cafe babe沒變。 很多文件存儲標准中都用魔數進行身份標識,如圖片gif,jpeg都在文件頭部中存儲着魔數。

5、jvm常量池

我先講下概念,接下來我會將class文件轉為16進制流后,在舉例說明。

常量池中每一項常量都是一個表,jdk1.8有14種結構不同的表結構,這14個表有個共同特點,就是表開始的第一位都是一個u1類型的標志位,JVM根據這個標志位[tag]來確定某個常量池項表示什么類型的字面量,比如tag為1就是指CONSTANT_utf8_info

再看常量池類型表

這14種常量項結構還有一個特點是,其中13表占用得字節固定,只有CONSTANT_Utf8_info占用字節不固定,其大小由length決定。為什么呢?因為從常量池存放的內容可知,其存放的是字面量和符號引用,最終這些內容都會是一個字符串, 這些字符串的大小是在編寫程序時才確定,比如你定義一個類,類名可以取長取短,所以在沒編譯前,無法確定大小不固定,編譯后,通過utf-8編碼,就可以知道其長度。

在看每一項常量表對應的說明:


二、16進制class文件解析

先看java代碼

package com.jincou.demo.domain;
public class XiaoXiao {
    private String father;
    public String fatherName() {
        return "小小她爹";
    }
}

通過命令自動生成class文件(會在同一目錄生成)

javac XiaoXiao.java

在將class文件拖入文本編輯器里,顯示自然就是16進制流了,如下:

上面的表其實可以划分為以下七個部分,.class 字節碼文件包括:

  • 魔數與class文件版本
  • 常量池
  • 訪問標志
  • 類索引、父類索引、接口索引
  • 字段表集合
  • 方法表集合
  • 屬性表集合

這篇博客只講到常量池,其它的下篇講,接下來我們一行一行解釋,首先是:

cafe babe:上面說過了這個是魔數,告訴JVM虛擬機我就是class文件。

0000 0034:次版本號組成u2+主版本號u2。共占4個字節。0034轉10進制為52,代表當前JDK版本為1.8。

0013 :說明有19-1即18個常量。

上面這些位置是固定的。接下來就是說明每一個常量:

0a:這就是tag代表一個標志,0a代表10,去找常量池列表。

得知它是一個接口中方法的符號引用,然后去找CONSTANT_Methodref_info對應常量列表描述:

從常量列表我們可以知道該類型一共占了5u,即0a00 0400 0f,那么下一個tag就是08代表字符串類型常量,以此類推就可以知道一共18個常量的信息。

三、class反編譯

通過上面看16進制的卻太麻煩了,現在我們可以通過JDK自帶反編譯工具查看會更加清晰。

javap -verbose 文件名

通過反編譯看去就很直觀,比如第一個字符常量很明顯告訴你是CONSTANT_Methodref_info,而且對於的就是4和15和上面完美對應。

最后思考,到底哪些會放到常量池?

1.常量池可以理解為class文件中的資源倉庫,有很多種類型,主要存放兩大常量
①.字面量 
字面量就是通俗理解的java常量,如文本字符串,8大基本數據類型,final修飾的常量值等
②.符號引用
符號引用屬於編譯原理的概念,主要包含以下三種
1)類和接口的全限定名
2)字段的名稱和描述符
3)方法的名稱和描述符

參考

1、深入了解java虛擬機第2版第六章

2、深入理解JVM-Class文件結構和類加載

3、深入理解JVM之Java字節碼(.class)文件詳解



只要自己變優秀了,其他的事情才會跟着好起來(少將3)


免責聲明!

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



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