《深刻理解Java虚拟机》第2版挖的坑终于在第3版中被R大填平了

这是why技术的第34篇原创文章java

本周仍是在家办公的一周,上面的图就是我在家的工位,和上周《Dubbo Cluster集群那点你不知道的事》这篇文章里面的第一张图片比起来,升级了显示器支撑臂,若是短还能够加长;用上了机械键盘,让指尖享受那一点点来自红轴的美妙反馈......程序员

仍是那句话:工欲善其事,必先利其器。在家办公,我是认真的。web

图中显示器下面的两本书分别是《深刻理解Java虚拟机》的第2版和第3版。也就是本文的主角。编程

你的手边有第2版吗?jvm

来,翻到第57页。这里面有个“坑”,看看你当时发现了没,有没有在这页作笔记呢?编程语言

没有也不要紧,我带你先回顾一下这一页的内容,再让你看看我三年前第一次看这书的时候作的笔记。编辑器

第2版57页讲了啥?

也许你根本就没看过《深刻理解Java虚拟机(第2版)》这本书。可是你必定见过位于本书第57页的示例代码:学习

因为JDK 6常量池位于方法区,JDK 7之后常量池位于堆中,因此用两个版本的jdk跑上面的代码就会出现神奇的事情。甚至用JDK 8来跑,也会出现你想不到的结果。且听我慢慢道来。ui

先说一下intern是干啥的。搜索引擎

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

再看一下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大最终的答案,具体探索的过程去看他的回答吧,很强很硬核:

咱们能够看到sun.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技术】,坚持输出原创。分享技术、品味生活,愿你我共同进步。

相关文章
相关标签/搜索