運行時常量池,之前放在方法區(永久代)中,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