深入理解java虚拟机关于运行时常量池的描述有错误?

深入理解java虚拟机关于运行常量池的描述有错误?

问题起因

近日前公司技术群突然引发了一个问题的讨论,这个问题是关于周志明老师的深入理解java虚拟机第三版2.2.6章节关于运行时常量池的一段描述(运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。),提出这个问题的技术人员的质疑是:运行时常量池和字符串常量池没有什么关系,String.intern()方法操作的是字符串常量池,并且对比了jvms在GitHub上与周志明老师也唇枪舌战了一番。

  • 围观地址:github.com/fenixsoft/j… 下面是部分论证的截图,各自的论据感兴趣的可以去围观一下。

image.png

image.png

为此我们俩也简单讨论了一下(贴几张图)

image.png

image.png

他是对比jvms没有明确定义,在质疑那段话的准确性,我同样也有疑问,字符创常量池规范都没有提及,你为什么要作为论据?难道要以实现反推规范?并且周志明老师是深入理解java虚拟机,不是深入理解HotSpot虚拟机的实现,这本书其实是从大的方面包括规范、实现来讲述的。所以带着点疑惑我们也简单讨论了一下,最后谁也没说服谁。

我的理解

  • 从上面可以看出我的这位前同事的核心论据是什么,围绕的核心是什么?
  1. 字符串常量池,以及字符串量池在Hotspot虚拟中的实现(java8之后字符串常量池在堆空间、方法区在元空间)
  2. jvms中没有明确说明String的intern()方法就一定操作的是运行时常量池(确实jvms8、11、17等在5.1章节中没有任何明确的话就说明了一定操作的是运行时常量池,只是结合JLS简单描述了一下交互和例子说明)
  3. 运用String.valueOf().intern() 与带双引号的字符串进行 == 比较进行简单分析
  4. 在就是jvms中没有一句话是明确说明到底操作的是谁

接下来我说一下我的个人观点,因为我们俩也简单论证了一会,只是最终都没有说服对方 我比较不服的点在于规范中没有的不能作为依托于规范的论据,字符串常量池jvms中都没有相关定义,那么你凭什么说它逻辑上不属于运行时常量池呢?这是他论据的一个反逻辑。还有就是使用HotSpot虚拟机码代码论证。

  • 首先是jvms2.2.6章节中的描述最后两句所要表达的意思你是怎么理解的?

从这个方面我简单描述一下,最后两句是(并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入中,这种特性被开发人员利用得比较多的便是String类的intern()方法。),我加重了几个词,其实我前同事的理解加粗的池是运行时常量池,以及String的intern()方法的动态性操作的就是这个池(运行时常量池),然后实际上intern()操作的是字符串常量池,规范中并没有那句话说明字符串常量池属于运行时常量池,所以周志明老师说的有问题。那么我是不是也可以这么理解,这两句的重点在于这种特性 (什么特性?运行期间动态的将数据放入池中),那么接下来我就可以说,intern()运用了这种特性,将字符串动态的操作于字符串常量池,其实这句话我个人感觉还是要看个人理解的,我也可以理解我所描述的那样,而不是我同事所理解的。当然这么说就是在拼语文,并不能作为搬倒别人思想的论据,接下来说一下我的论据。

  • 在jvms(以8版本为例,其实其他的我看了也差不多)中对于 Run-Time Data Areas(运行时数据区)中对于Heap(堆)、Method Area(方法区)、Run-Time Constant Pool(运行时常量池)的描述中有过这样的描述(如图所示),在jvms2.5章节下:

image.png

image.png

image.png

得出的结论是:逻辑上方法区属于堆的一部分,运行时常量池属于方法区的一部分,所以逻辑上运行时常量池也算是属于堆的一部分。

那么具体的逻辑关系是不是就可以看作是我画的图所示的样子。

image.png

所以结合jvms可以看出,逻辑上的划分和具体的虚拟机的实现是有差别的,不能以偏概全,也不能拿具体的实现来反推规范而作为论据,因为一切java虚拟的实现都要遵从java虚拟机的开发规范,就算实现上有差异,那么逻辑上也是遵守jvms的。

  • jvms中并没有对于字符串常量池的定义,那么字符串常量池这个概念其实是不是算是HotSpot虚拟机自己具化出来的,为了高效实现符合规范定下的一种对于字符串动态缓存的具体实现呢?因为HotSpot确实是业界主流,所以往往很多时候提及的java虚拟机的很多细节全部都是按照HotSpot虚拟机的实现来进行分析的。而周志明老师的深入理解java虚拟机其实不能把思想完全固化在HotSpot虚拟机,要结合jvms和主流的虚拟机一起来分析学习。

  • 接下来结合jvms分析一下规范中所提及到的

    • The Constant Pool(常量池):感兴趣的javap -v xxx.class 可以看看 constant pool部分
    • The Run-time Constant Pool(运行时常量池):将Constant Pool中的各种字面量和符号引用,在类加载后存放在方法区的运行时常量池中,所以具备动态性,是运行时所进行的操作。其中jvms中关于intern()的描述也是在5.1章节The Run-time Constant Pool中,描述下图所示。可见在规范的角度,可见intern()与运行时常量池的交互。在5.1章节
    • jvms对于运行时常量池的实现细节上是没有相关规范的,所以这就对于虚拟机的具体实现上有着足够的发挥空间,例如HotSpot虚拟机

image.png

  • 我为什么认为字符串常量池逻辑上算为运行时常量池的一部分?
    • 特性:字符串常量池也拥有运行时动态添加数据的特性
    • jvms规范中有定义,逻辑上方法区属于堆,运行时常量池属于方法区,那么针对于HotSpot虚拟机(java8为例),字符串常量池在堆,方法区在元空间,运行时常量池在元空间,虽然物理上不在一块,但是按照jvms规范,逻辑上其实还是在堆上,逻辑上并未分离。就如上面的逻辑关系图所示。
    • 字面量(String Literal)以CONSTANT_String_info结构存储在运行时常量池中。

所以,我个人认为不能依托于虚拟机具体的实现来反推深入理解java虚拟机中的描述。 那么我换一种方式理解,还是重点词在于这种特性,那么HotSpot虚拟机的字符串常量池应用了这种动态性,动态的将字符串放入池中,那么是不是按照我前同事的理解,其实这段话也不是问题了?

我的个人想法结论,这段话没有问题,字符串常量池逻辑概念上应该属于运行时常量池的一部分

针对这个问题,其实不同的解读会产生不同的想法,正是因为jvms、jls都没有明确的定义而已,所以才产生了各种各样的想法,这也正是技术进步的一种方式吧,众说纷纭,各取所长,各自发挥。

所以,你们针对这个说法觉得有问题么?或者有更强大的论据,我们可以探讨一下,共同进步。

猜你喜欢

转载自juejin.im/post/7049205638563364872
今日推荐