运行时常量池中的符号引用/String.intern() /ldc指令

时间:2022-06-11 19:38:05

运行时常量池,之前放在方法区(永久代)中,1.8之后被转移到元空间,放到了native memory中。

具体的数据结构是:(看对象的内存布局,句柄访问还是对象头中保存指向类的元数据的指针,这里以对象头markword之后保存指向元数据指针为例)对象有一个指向类元数据的指针,指向的这个数据结构InstanceClass,InstanceKlass有一个指针指向一个constantPool数据结构(运行时常量池),这个数据结构是一个数组,数组里面元素的排列方式和class文件中是一样的(从1号常量到N号常量),只不过值有变化,7-18里面保存的是数字,根据规则拆开指向的是1-6类型的序号,而1-6类型保存的指向symbol结构体的指针,在class文件中,1-6指向的内容是class文件单独的,而在运行时常量池中,指向的symbol结构体是可以共享的,能做到这一点是因为在引用symbol结构体之前,要经过一个hashTable存取(这个和我们从hashmap中get push的原理差不多),这个hashTable叫symbolTable,存放在元空间,而每个symbol的结构体也存放在元空间。

在ldc命令(以类型1的utf-8为例)中,如果ldc后面的两个字节指向的符号引用没有被解析过,指向的必然是symbol结构体在自己的class对应的运行时常量池中的序号,那么需要首先去StringTable(和symbolTable一样的一个hashTable,存放在元空间中,保存着指向堆中的String对象的引用)找和symbol结构体相等的String对象,如果找到,把自己后面两个字节保存这个String对象的引用,然后把这个string对象的引用放入栈顶。如果没找到,从刚才说的symbol结构体中(下面的红色字体)解析出String对象(这里要先创建一个char数组对象,棕色字体),并放到字符串常量池(StringTable)中(粉色字体,intern方法),然后两个字节替换成引用,入栈。

而String.intern()方法是根据一个String对象去StringTable中找,找到了,方法返回里面对象的引用,找不到,放进去,返回引用。所以返回值是不是原String看之前有没有

oop StringTable::intern(Symbol* symbol, TRAPS) {
if (symbol == NULL) return NULL;
ResourceMark rm(THREAD);
int length;
jchar* chars = symbol->as_unicode(length); // 从 Symbol 中解析出字符串字面量
Handle string;
oop result = intern(string, chars, length, CHECK_NULL); // 调用又一个同名方法
return result;
}

字符串拼接,字节码分析可以见这篇文章:https://www.cnblogs.com/Kidezyq/p/8040338.html