如果要看懂这两行代码,我们需要对常量池中String类型常量的结构有一定了解,其结构如下:
CONSTANT_String_info tag 标志常量类型的标签index 指向字符串字面量的索引
对应到我们上面的字节码中,tag=String,index=#14,所以我们可以知道,#2是一个字面量为#14的字符串类型常量。而#14对应的字面量信息(一个Utf8类型的常量)就是dmz。
常量池作为资源仓库,最大的用处在于被class文件中的其它结构所引用,这个时候我们再将注意力放到main方法上来,对应的就是这三条指令
0: ldc #2 // String dmz 2: astore_1 3: returnldc:这个指令的作用是将对应的常量的引用压入操作数栈,在执行ldc指令时会触发对它的符号引用进行解析,在上面例子中对应的符号引用就是#2,也就是常量池中的第二个元素(这里就能看出方法表中就引用了常量池中的资源)
astore_1:将操作数栈底元素弹出,存储到局部变量表中的1号元素
return:方法返回值为void,标志方法执行完成,将方法对应栈帧从栈中弹出
下面我用画图的方式来画出整个流程,主要分为四步
解析ldc指令的符号引用(#2)
将#2对应的常量的引用压入到操作数栈顶
将操作数栈的元素弹出并存储到局部变量表中
执行return指令,方法执行结束,弹出栈区该方法对应的栈帧
第一步:
在解析#2这个符号引用时,会先到字符串常量池中查找是否存在对应字符串实例的引用,如果有的话,那么直接返回这个字符串实例的引用,如果没有的话,会创建一个字符串实例,那么将其添加到字符串常量池中(实际上是将其引用放入到一个哈希表中),之后再返回这个字符串实例对象的引用。
到这里也能回答我们之前提出的那个问题了,一个对象是new出来的,另外一个是在解析常量池的时候JVM自动创建的
第二步:
将第一步得到的引用压入到操作数栈,此时这个字符串实例同时被操作数栈以及字符串常量池引用。
第三步: