运行时常量池,之前放在方法区(永久代)中,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对象在常量池中的序号,那么需要首先去StringTable(和symbolTable一样的一个hashTable,存放在元空间中,保存着指向堆中的String对象的引用)找equal自己指向的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