搜尋
首頁JavaJava基礎java常量池圖文詳解

java常量池圖文詳解

Nov 30, 2019 pm 04:36 PM
java

java常量池圖文詳解

java常數池是一個經久不衰的話題,也是面試官的最愛,題目花樣百出,小菜早就對常量池有所耳聞,這次好好總結一下。

推薦:java影片教學

jvm虛擬記憶體分佈:

java常量池圖文詳解

程式計數器是jvm執行程式的流水線,存放一些跳躍指令,這個太高深,小菜不懂。

本機方法堆疊是jvm呼叫作業系統方法所使用的堆疊。

虛擬機器堆疊是jvm執行java程式碼所使用的堆疊。

方法區存放了一些常數、靜態變數、類別資訊等,可以理解成class檔案在記憶體中的存放位置。

虛擬機器堆是jvm執行java程式碼所使用的堆。

Java中的常數池,實際上分為兩種形態:靜態常數池和執行時間常數池。

所謂靜態常數池,即*.class檔案中的常數池,class檔案中的常數池不只包含字串(數字)字面量,還包含類別、方法的信息,佔用class檔案絕大部分空間。

 而運行時常數池,則是jvm虛擬機器在完成類別裝載作業後,將class檔案中的常數池載入到記憶體中,並保存在方法區中,我們常說的常數池,就是指方法區中的運行時常量池。

接下來我們引用一些網路上流行的常數池例子,然後藉以講解。

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
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

首先說明一點,在java 中,直接使用==運算符,比較的是兩個字串的參考位址,並不是比較內容,比較內容請用String.equals()。

s1 == s2這個非常好理解,s1、s2在賦值時,均使用的字串字面量,說白話點,就是直接把字串寫死,在編譯期間,這種字面量會直接放入class檔案的常數池中,從而實現重複使用,載入運行時常數池後,s1、s2指向的是同一個記憶體位址,所以相等。

s1 == s3這個地方有個坑,s3雖然是動態拼接出來的字串,但是所有參與拼接的部分都是已知的字面量,在編譯期間,這種拼接會被優化,編譯器直接幫你拼好,所以String s3 = "Hel" "lo";在class檔案中被最佳化成String s3 = "Hello";,所以s1 == s3成立。

s1 == s4當然不相等,s4雖然也是拼接出來的,但new String("lo")這部分不是已知字面量,是一個不可預料的部分,編譯器不會優化,必須等到運行時才可以確定結果,結合字串不變定理,鬼知道s4被分配到哪裡去了,所以地址肯定不同。配上一張簡圖理清思路:

java常量池圖文詳解s1 == s9也不相等,道理差不多,雖然s7、s8在賦值的時候使用的字符串字面量,但是拼接成s9的時候, s7、s8作為兩個變量,都是不可預料的,編譯器畢竟是編譯器,不可能當解釋器用,所以不做優化,等到運行時,s7、s8拼接成的新字符串,在堆中地址不確定,不可能與方法區常數池中的s1位址相同。

java常量池圖文詳解

s4 == s5已經不用解釋了,絕對不相等,二者都在堆中,但位址不同。

s1 == s6這兩個相等完全歸功於intern方法,s5在堆中,內容為Hello ,intern方法會嘗試將Hello字串添加到常數池中,並返回其在常數池中的位址,因為常數池中已經有了Hello字串,所以intern方法直接回傳位址;而s1在編譯期就已經指向常數池了,因此s1和s6指向同一位址,相等。

至此,我們可以得到三個非常重要的結論:

#必須專注於編譯期的行為,才能更好的理解常數池。

運行時常數池中的常數,基本上來自各個class檔案中的常數池。

程式執行時,除非手動在常數池中新增常數(例如呼叫intern方法),否則jvm不會自動加入常數到常數池。

以上所講只涉及字串常數池,實際上還有整數常數池、浮點型常數池等等,但都大同小異,只不過數值類型的常數池不可以手動添加常數,程式啟動時常數池中的常數就已經確定了,例如整型常數池中的常數範圍:-128~127,只有這個範圍的數字可以用到常數池。

實務

說了這麼多理論,接下來讓我們觸摸真正的常數池。

前文提到過,class檔案中存在一個靜態常數池,這個常數池是由編譯器產生的,用來儲存java原始檔中的字面量(本文僅關注字面量)​​,假設我們有如下java程式碼:

 String s = "hi";

为了方便起见,就这么简单,没错!将代码编译成class文件后,用winhex打开二进制格式的class文件。如图:

java常量池圖文詳解

简单讲解一下class文件的结构,开头的4个字节是class文件魔数,用来标识这是一个class文件,说白话点就是文件头,既:CA FE BA BE。

紧接着4个字节是java的版本号,这里的版本号是34,因为笔者是用jdk8编译的,版本号的高低和jdk版本的高低相对应,高版本可以兼容低版本,但低版本无法执行高版本。所以,如果哪天读者想知道别人的class文件是用什么jdk版本编译的,就可以看这4个字节。

接下来就是常量池入口,入口处用2个字节标识常量池常量数量,本例中数值为00 1A,翻译成十进制是26,也就是有25个常量,其中第0个常量是特殊值,所以只有25个常量。

常量池中存放了各种类型的常量,他们都有自己的类型,并且都有自己的存储规范,本文只关注字符串常量,字符串常量以01开头(1个字节),接着用2个字节记录字符串长度,然后就是字符串实际内容。本例中为:01 00 02 68 69。

接下来再说说运行时常量池,由于运行时常量池在方法区中,我们可以通过jvm参数:-XX:PermSize、-XX:MaxPermSize来设置方法区大小,从而间接限制常量池大小。

假设jvm启动参数为:-XX:PermSize=2M -XX:MaxPermSize=2M,然后运行如下代码:

//保持引用,防止自动垃圾回收
List<String> list = new ArrayList<String>();
        
int i = 0;
        
while(true){
    //通过intern方法向常量池中手动添加常量
    list.add(String.valueOf(i++).intern());
}

程序立刻会抛出:Exception in thread "main" java.lang.outOfMemoryError: PermGen space异常。PermGen space正是方法区,足以说明常量池在方法区中。

在jdk8中,移除了方法区,转而用Metaspace区域替代,所以我们需要使用新的jvm参数:-XX:MaxMetaspaceSize=2M,依然运行如上代码,抛出:java.lang.OutOfMemoryError: Metaspace异常。同理说明运行时常量池是划分在Metaspace区域中。具体关于Metaspace区域的知识,请读者自行搜索。

更多java知识请关注java基础教程栏目。

以上是java常量池圖文詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:博客园。如有侵權,請聯絡admin@php.cn刪除
Java中有哪些不同的垃圾收集算法(串行,並行,CMS,G1,ZGC)?Java中有哪些不同的垃圾收集算法(串行,並行,CMS,G1,ZGC)?Mar 14, 2025 pm 05:06 PM

本文討論了各種Java垃圾收集算法(串行,並行,CMS,G1,ZGC),它們的性能影響和適合大量堆的應用。

什麼是Java虛擬機(JVM),它在內部如何工作?什麼是Java虛擬機(JVM),它在內部如何工作?Mar 14, 2025 pm 05:05 PM

本文討論了Java虛擬機(JVM),詳細介紹了其在不同平台運行Java程序中的作用。它說明了JVM的內部流程,密鑰組件,內存管理,垃圾收集和性能Optimizatio

如何使用Java的Nashorn Engine用JavaScript腳本?如何使用Java的Nashorn Engine用JavaScript腳本?Mar 14, 2025 pm 05:00 PM

Java的Nashorn Engine可以在Java應用程序中啟用JavaScript腳本。關鍵步驟包括設置Nashorn,管理腳本和優化性能。主要問題涉及安全性,內存管理和未來兼容性

如何使用Java的Try-with-Resources語句進行自動資源管理?如何使用Java的Try-with-Resources語句進行自動資源管理?Mar 14, 2025 pm 04:59 PM

Java的Try-with-Resources通過自動關閉文件流或數據庫連接等資源來簡化資源管理,從而提高代碼可讀性和可維護性。

如何使用Java的枚舉來表示固定的值集?如何使用Java的枚舉來表示固定的值集?Mar 14, 2025 pm 04:57 PM

Java枚舉代表固定的值集,通過自定義方法和構造函數提供類型安全性,可讀性和其他功能。它們增強了代碼組織,可用於開關語句中以進行有效的價值處理。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境