《深刻理解Java虛擬機》第2版挖的坑終於在第3版中被R大填平了

這是why技術的第34篇原創文章html

本週仍是在家辦公的一週,上面的圖就是我在家的工位,和上週《Dubbo Cluster集羣那點你不知道的事》這篇文章裏面的第一張圖片比起來,升級了顯示器支撐臂,若是短還能夠加長;用上了機械鍵盤,讓指尖享受那一點點來自紅軸的美妙反饋......java

仍是那句話:工欲善其事,必先利其器。在家辦公,我是認真的。程序員

圖中顯示器下面的兩本書分別是《深刻理解Java虛擬機》的第2版和第3版。也就是本文的主角。編程

你的手邊有第2版嗎?app

來,翻到第57頁。這裏面有個「坑」,看看你當時發現了沒,有沒有在這頁作筆記呢?jvm

沒有也不要緊,我帶你先回顧一下這一頁的內容,再讓你看看我三年前第一次看這書的時候作的筆記。編程語言

第2版57頁講了啥?

也許你根本就沒看過《深刻理解Java虛擬機(第2版)》這本書。可是你必定見過位於本書第57頁的示例代碼:學習

因爲JDK 6常量池位於方法區,JDK 7之後常量池位於堆中,因此用兩個版本的jdk跑上面的代碼就會出現神奇的事情。甚至用JDK 8來跑,也會出現你想不到的結果。且聽我慢慢道來。ui

先說一下intern是幹啥的。this

該方法的做用是把首次遇到的字符串加載到常量池中

再看一下intern的註釋:

其中標記了☆的紅框翻譯過來就是:對於任意兩個字符串 s 和 t,當且僅當 s.equals(t) 爲 true 時,s.intern() == t.intern() 才爲 true。

回到最開始的代碼中。引用第三版的描述以下:

這段代碼在JDK 6中運行,會獲得兩個false,而在JDK 7中運行,會獲得一個true和一個false。

產生差別的緣由是:在JDK 6中,intern()方法會把首次遇到的字符串實例複製到永久代的字符串常量池中存儲,返回的也是永久代裏面這個字符串實例的引用,而由StringBuilder建立的字符串實例在Java堆上,因此必然不是同一個引用,將返回false。

而JDK 1.7(以及部分其餘虛擬機,例如JRocki)的intern()實現就不須要再拷貝字符串的實例到永久代了,既然字符串常量池已經移到了Java堆中,那隻須要在常量池裏記錄一下首次出現的實例引用便可,所以intern()返回的引用和由StringBuilder建立的那個字符串實例就是同一個。

對str2比較返回false是**由於「java」這個字符串在執行StringBuilder.toString()以前已經出現過,**字符串常量池中已經有它的引用了,不符合intern()方法要求「首次出現」的原則,而「計算機軟件」這個字符串則是首次出現的,所以返回true。

挖坑不填,坑哭讀者

讀到這裏你有沒有一些不惑呢?有沒有感受到一絲絲不對呢?

咱們再看看原文:

爲何在JDK 7裏面會返回fasle,上面紅框框起來的部分是關鍵答案:

由於「java」這個字符串在執行StringBuilder.toString()以前已經出現過。

這句話就是「坑」,已經出現過?在哪出現的,你卻是告訴我啊!我當時的心裏想法和下面的老大哥是同樣同樣的:

我第一次看這本書是在2016年,看這個地方的時候,我就百思不得其解,在哪就出現過了呀?

當時也不知道是在哪一個寫的似是而非的博客裏面找到「java是關鍵字,已存在常量池中」這句「騷話」。還正正經經的抄了上去,雖然是錯誤的描述,雖然字是醜了點......

你當年或者如今看的時候有這個疑惑嗎?

以後我又完整的看過幾回這本書,我清楚的記得,我再一次看到這裏的時候我就以爲**「java是關鍵字,已存在常量池中」這個描述是不對的**,因此我在後面打了一把叉,再次去找了相關資料,找到了sun.misc.version,終於解決了「在哪出現的」這個問題。

第3版註腳填坑

這個2013年(第二版出版那年)挖下的坑,在2016年10月1日,就被R大在知乎上給填上了。R大的這個回答也被做者周志明寫在了2019年末出版的《深刻理解Java虛擬機(第三版)》的註腳裏面:

裏面的RednaxelaFX就是R大,一個把虛擬機玩到極致,憑一己之力撐起了知乎java半邊天的男人,後面我會詳細介紹一下的。

你只要瞭解到一點就行:他的回答,就是權威。

在R大的這個知乎回答中,幫周志明大大填了這個坑,我強烈建議你必定要去看看,連接以下

https://www.zhihu.com/question/51102308/answer/124441115

R大幫忙填坑

我這裏只是結合R大的回答和我的的一點點經驗,談談本身的認知。

在2016年我讀這本書的時候,我纔剛剛大學畢業,剛重新手村出來。當時的我對於這個問題是絕對沒有任何思路的,必須直接在網上查詢答案。

如今的我,略有一點經驗,再次遇到這個問題,就算沒看書中的描述、R大的回答,我確定也會想到:在書中的示例裏面,第二個輸出false,說明調用main方法以前,確定在字符串常量池裏面已經有了這個「java」字符串了。

怎麼驗證一下呢?

咱們在main方法的第一行打上一個斷點,debug運行程序後,能夠看到Memory,而後過濾出String,以下:

而後雙擊過濾出來的java.lang.String,能夠看到下圖:

在這個頁面咱們能夠繼續過濾:

果真,在程序還沒執行第14行以前,「java」已經出現了。

從這個結果咱們能夠推斷出:Java標準庫在JVM啓動過程當中加載的部分,可能裏面就有類裏有引用「java」字符串字面量,這個字面量被初次引用的時候就會被intern,加入到字符串常量池中去。

**而究竟是哪一個類致使了這個「java」字符串被intern的呢?**R大主要就是回答了這個問題。

我截取一下R大最終的答案,具體探索的過程去看他的回答吧,很強很硬核:

咱們能夠看到sum.misc.Version裏面的launcher_name字段的值就是「java」:

而根據R大的回答,咱們能夠找到java.lang.System類:

根據System類的註釋咱們能夠知道,它是由虛擬機自動調用的。而其initializeSystemClass方法會調用sun.misc.Version.init()方法。

到此就真相大白了。

Java標準庫在JVM啓動過程當中會調用sun.misc.Version的init()方法。因此sun.misc.Version會進行類加載的操做,而類加載的初始化階段時,會對靜態常量字段進行真正的賦值操做,可是因爲sun.misc.Version的launcher_name字段是final修飾的,因此引用的字符串「java」在準備階段就被intern到了字符串常量池裏面了。

能夠在內心在默默的複習一下類加載的過程:加載、驗證、準備、解析和初始化這五個階段哦。

另外書中給出的示例代碼也有必定的侷限性,R大是這樣說的:

其實這事情很簡單:首先,這個行爲必然是要針對某個具體的JDK/JRE實現來討論的,由於Java語言規範/JVM規範/Java SE標準庫的JavaDoc(也是Java SE平臺規範的一部分)都沒有、也不會強制指定哪一個類裏必定要引用「java」這個字符串常量,並且它必須是第一個使得「java」被intern的類 --- 規定這個也太無聊了。

好比這個示例我在JDK8u212-b03上跑出來,就是兩個true:

在這個版本里面,sun.misc.Version的launcher_name變成了「openjdk」:

那麼根據咱們以前的猜想,把程序成下面這樣的,效果就是同樣的了:

萬變不離其宗,如今你知道爲何這裏用openjdk返回也是false了吧。

知其然,還要知其因此然。

R大與周志明之間的「愛恨情仇」

R大是誰?

我先上一張《深刻理解java虛擬機(第二版)》背面的一張圖吧,R大給這本書寫過推薦語:

莫樞(RednaxelaFx)Oracle HotSpot VM編譯器團隊工程師。(如今他已經不在Oracle了。據網上公開資料,R大是前阿里巴巴技術專家,前Oracle JVM核心開發,前Azul核心開發,現就任於Databricks)

再看一下他的知乎主頁:https://www.zhihu.com/people/rednaxelafx/answers

你去知乎上只搜RednaxelaFX(甚至直接用搜索引擎搜索),就能搜到不少結果,我隨便截取一個片斷。

在【有哪些頂級水平的中國程序員?】這個話題下,有一個回答只是@一下R大的ID,沒有多說一個字,就得到了258個贊,評論中也盡是讚美的語言,乾貨多,就是他的特色:

他與《深刻理解Java虛擬機》的做者周志明大大,在2010年到2011年間,在iteye上已經有過屢次深度交流,好比下面的吐槽:

好比下面的調侃:

玩歸玩,鬧歸鬧,周志明也直言閱讀了R大的不少文章,受益良多:

而且在書裏的致謝章節專門謝謝了R大:

說這麼多,我想要表達的觀點其實就是一個:

R大是一個寶藏啊,他樂於分享和交流,憑藉一己之力推進了國內jvm的學習和研究,若是你想要了解虛擬機、編譯原理和編程語言方面的相關知識,他是一個你繞不過的人。他值得被更多的程序員知道。

若是你以前不知道,但看了我這篇文章後知道了他,個人目的就達到了。

他在知乎上認認真真碼字,用心的對待每個回答,他是一個"碼"宗強者,恐怖如斯,可是從他的各類回答、博客文章中,你能夠感受到謙遜、細緻、系統、耐心、專業、嚴謹.....就像一個評論說的:

在技術圈日益浮躁的今天,感受他就是主席所說的那種:一個純粹的人,一個有道德的人,一個脫離了低級趣味的人,一個有益於人民的人。

咱們作程序的,要向他學習,向他致敬。

最後再附上一個R大的資料合集連接吧,全是寶藏,待你去發掘:https://zhuanlan.zhihu.com/p/25042028

最後說一句

才疏學淺,不免會有紕漏,若是你發現了錯誤的地方,還請你留言給我指出來,我對其加以修改。

若是你以爲文章還不錯,你的轉發、分享、點贊、留言就是對我最大的鼓勵。

感謝您的閱讀,我堅持原創,十分歡迎並感謝您的關注。

以上。

歡迎關注公衆號【why技術】,堅持輸出原創。分享技術、品味生活,願你我共同進步。

原文出處:https://www.cnblogs.com/thisiswhy/p/12317742.html

相關文章
相關標籤/搜索