-
Notifications
You must be signed in to change notification settings - Fork 222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
2.2.6运行时常量池与字符串常量池应该不是同一个池 #112
Comments
string iteral pool是jvm runtime constant pool的一个逻辑部分,在《JVMS》对runtime constant pool的定义中明确声明了这一点: 这段(以及后面未贴出的一页)正好解释了String.intern()方法与runtime constant pool的交互。 Issues中提到的:
似乎混淆了constant pool和runtime constant pool的概念,前者是定义在Class文件格式之中,存储的Class文件中直接使用到的,编译期已经静态可知的符号,后者是定义在虚拟机类加载(Loading、Linking、Initalizing)行为之中,存储的不仅包括constant pool中的静态符号,还包括了程序运行期动态产生的符号,所以带有“runtime”的定语。它们两者是具有不同作用的独立概念。 |
周老师,你好,感谢回复。
翻译为中文为
至于您截图的内容,只是为了表示string literal表述的内容,由于对string literal表述过多,容易喧宾夺主,产生字符串常量池是运行时常量池一部分的误导。 根据2018年的jvms11表述 翻译为中文为 一个字符串常量是String类的实例对象引用,派生于 CONSTANT_String_info结构。派生一个字符串常量,jvm会检查 CONSTANT_String_info中给出的code points序列: 翻译的比较拙劣,请见谅。但基于jvms的表述,我还是对以下说法有不同看法。
而且现在字符串常量池已经不在方法区了。 |
有不同意见是好事,欢迎讨论。
这个结论并不正确,我明白你想说的是HotSpot在JDK8之后的变动,是指:“JDK 8后,HotSpot虚拟机已经不再使用以前PermGen来实现方法区了,类型信息挪到Metaspace,类变量、字面量等信息挪到了Heap中了”。 但后面这个事实并不能推导出前面的结论,运行时常量池、方法区、字符串常量池这些都是不随虚拟机实现而改变的逻辑概念,是公共且抽象的,Metaspace、Heap是与具体某种虚拟机实现相关的物理概念,是私有且具体的。 虚拟机规范提倡的理念是“公有设计,私有实现”,我们可以说“目前版本的HotSpot没有将字符串常量池放在元空间中",但是不能说“现在的JVM不再将字符串常量池放在方法区中”。无论是哪一种Java虚拟机,无论它是使用PermGen(8之前的HS),还是JavaHeap(8之后的HS),抑或是CHeap(J9)来存放字面量,都不影响字符串常量仍然是运行时常量的一个子集,因为它们都是逻辑上的概念,所有Java虚拟机一致的外观。 其实“字符串常量池”这个说法,也是因为HotSpot本身实现了这个概念,大家已经形成习惯。其实在JVMS中只陈述了字面量应该不重复地存储在运行时常量池之中,并没有定义过字符串常量“池”的概念。但有一点是明确的,无论我回帖贴截图中《JVMS 15》的表述,还是你翻译的《JVMS 11》的表述,都规定了:
最后,关于这句话:“并非预置入Class文件中常量池的内容才能进入方法区运行时常量池”,具体是与翻译中的哪一部分相抵触? |
周老师,字符串常量池已经不在方法区了,我这个表述确实不恰当。 jvms和jls都只规定了
通篇并没有说字符串常量池,字符串常量池只是jvm的具体实现满足jvm对于字符串相关规定的解决方案。您在回贴中也有提到。您回贴中的表述我都认可,除了字符串常量池是运行时常量池的子集这个表述。 我反复看了几遍jvms11-5.1章节关于运行时常量池的描述,原文重点内容引用如下
此描述与您最后翻译的 刚刚我引用的原文,中文表述如下
感谢周老师不吝赐教,此问题已困扰我多年。 |
没那么夸张吧。“此问题”我看描述不知道具体是指哪个?先把“ 对于前者,这个结论是否能认可?
如果能认可,那字符串常量池就是虚拟机中存储字面量的集合,既然字面量是存储在运行时常量池的结构之一,那字符串常量池自然是它的子集。 对于后者,前面中文的说法有歧义,所指的就是你贴出的这行原文:
这个新创建的String实例就是字面量,关于String Literal的定义在JLS中有明确定义,它就是一个唯一的String实例。 |
周老师,这句话我认可。 但 字符串常量池就是虚拟机中存储字面量的集合 这句话不认可。 String str = "abc"; String str = String.valueOf(6666666).intern() 我感觉咱俩的主要分歧在于 String str = "abc";
|
JLS对于String Literal明确定义如下: 一个字面量是一个String对象实例的引用,而且相同的字面量指向的是同一个实例。 至于具体jvm怎么实现,jvms不管,满足上面两个要求即可。 根据我对jvms原文描述的理解,一言以蔽之,字符串常量池与运行时常量池无关,String.intern与运行时常量池无关。 |
上面这段描述(以及后面一贴在“一言以蔽之”之前的部分),并没有什么问题。然而我疑惑的是,你不认可的“字符串常量池就是虚拟机中存储字面量的集合”这个与描述中的内容的矛盾点具体是什么? 这句话与你给出的定义“ 字符串常量池是jvm具体实现(如HotSpot)中用于存放字符串实例对象的地方”,从文字结构来看,差别是宾语“存储字面量的集合”与“存放字符串实例对象的地方”。 退一步说,字符串常量池不是JVMS中的概念,针对虚拟机的抽象讨论中,可以不需要它,那回到最初的问题:
结合你的例子:
这里准确地说是没有静态常量(static constants)的字面量,在Class文件的常量池中只会存储数值型的6666666,不会存储字符串类型的“6666666”。但是运行期间是肯定会生成一个字符串类型的“6666666”。 所以先明确一下,你的观点是当运行到String.valueOf(6666666).intern()这样的语句时,虚拟机:
以上的哪一种?或者是在以上没有列举到的某一种? |
周老师,constant pool与runtime constant pool之间的关系如下:
这和jvms官方定义一致
字符串常量池对应的Hotspot实现类应该是symbolTable.cpp. 字面量!= 字符串实例。字符串实例是java对象,jvm在解析constant pool为runtime constant pool时,会将字面量解析为对应字符串实例的引用 当运行String.valueOf(6666666).intern()时,会产生一个“6666666”的字符串对象实例,但没有字面量,在字符串常量池中,但不在运行时常量池中。 String str = String.valueOf(6666666);
str.intern();
String str2 = "6666666";
System.out.print(str == str2); 周老师,上面这段代码应该输出什么? |
这部分问题依然是与之前一样,我们找资料摆论据,目的是为了证明自己的论点,以上贴出的资料时,你应该说明清楚的论点是什么?是以下:
或者是以上没有列举到的哪一种?
HotSpot中常量池的实现是src/hotspot/share/classfile/stringTable.cpp,symbolTable.cpp顾名思义是符号表,这里符号不是字符串的意思,是编译原理中的基础概念,程序中符号不仅仅包括字符串,其他的譬如关键字、变量名称、数值、运算符等等都是符号,注解甚至是注释如果有必要的话,都可以作为符号保留下来。
先从概念上看。
如果从概念入手,实在绕不出来的话,那退一步,容忍一些逻辑上的瑕疵,从具体代码入手来看看。 String.intern()方法的JNI入口在src/java.base/share/native/libjava/String.c中,如下: 经过若干次调用,最终的代码逻辑会调用前面提及的stringTable.cpp,方法如下: 对于直接存在于Class文件常量池中的静态常量,是通过ldc这个bytecode指令进入的,最终也会调用到同一个stringTable::intern()方法,堆栈如下: 可见,无论是直接在Java代码中存在的"6666666"静态常量,还是通过String.valueOf(6666666).intern()动态加进去的运行时常量,在HotSpot中最后都是在同一个地方存放的,在虚拟机视角一侧看来,没有什么区别。 基于这个基础,再来回看你的观点:
现在通过代码调试,可以证实stringTable.cpp中存储了确是是这个字符串的unicode表示,假设它真的独立于运行时常量池的话,你认为当Java代码中出现"6666666"静态常量时,譬如 |
周老师,与之交流,收益颇多。
|
周老师你好,我也认为单纯从《JVMS》这段描述,也无法说明"string iteral pool是jvm runtime constant pool的一个逻辑部分"。 我个人在看运行时常量池和字符串常量池时,也会感觉字符串常量池应该是一块独立的空间,和运行时常量池没有关系。运行时常量池单纯对应了.class文件中的常量池,只不过运行时常量池的内容会被解析成别的内容,之后运行时常量池的"项目数"不会增长。我们程序员自己去调用String.intern()也不应该动到运行时常量池,只是会用到字符串常量池。 |
2.2.6章节中描述
而在jvms8中对运行时常量池描述为
String
的intern
方法对应的是字符串常量池,一个定长的hashtable,在HotSpot jvm中,以前是在方法区中,1.7移到堆中,运行时常量池如果用javap查看class文件,可以看到Constant pool,这个应该才是jvms中提到的运行时常量池,您在书中也有提到。
综合来看,字符串常量池与jvm的运行时常量池并无关联。
个人见解,期待沟通!
The text was updated successfully, but these errors were encountered: