全局字符串常量池StringTable
以JDK7为例来分析
1、找到openjdk\jdk\src\share\native\java\lang\String.c文件,Java_java_lang_String_intern方法便对象java程序中的String.intern方法
JNIEXPORT jobject JNICALL Java_java_lang_String_intern(JNIEnv *env, jobject this) { return JVM_InternString(env, this); }
2、找到openjdk\hotspot\src\share\vm\prims\jvm.cpp文件,找到JVM_InternString方法
// String support JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) JVMWrapper("JVM_InternString"); JvmtiVMObjectAllocEventCollector oam; if (str == NULL) return NULL; oop string = JNIHandles::resolve_non_null(str); oop result = StringTable::intern(string, CHECK_NULL); return (jstring) JNIHandles::make_local(env, result); JVM_END
3、StringTable::intern方法便是核心,在openjdk\hotspot\src\share\vm\classfile\symbolTable.cpp中找到
oop StringTable::intern(oop string, TRAPS) { if (string == NULL) return NULL; ResourceMark rm(THREAD); int length; Handle h_string (THREAD, string); jchar* chars = java_lang_String::as_unicode_string(string, length);
// 调用了当前symbolTable.cpp文件中的重载方法 oop result = intern(h_string, chars, length, CHECK_NULL); return result; }
其实看自带的注释知道,在the_table中能找到字符串实例就返回,找不到就将字符串实例引用添加到the_table中;the_table是symbolTable.cpp中维护的全局变量
oop StringTable::intern(Handle string_or_null, jchar* name, int len, TRAPS) { unsigned int hashValue = java_lang_String::hash_string(name, len); int index = the_table()->hash_to_index(hashValue); oop string = the_table()->lookup(index, name, len, hashValue); // Found if (string != NULL) return string; // Otherwise, add to symbol to table return the_table()->basic_add(index, string_or_null, name, len, hashValue, CHECK_NULL); }
再看看,没找到字符串时添加到全局字符串常量池的代码,发现本质是一个HashtableEntry
oop StringTable::basic_add(int index, Handle string_or_null, jchar* name, int len, unsigned int hashValue, TRAPS) { debug_only(StableMemoryChecker smc(name, len * sizeof(name[0]))); assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(), "proposed name of symbol must be stable"); Handle string; // try to reuse the string if possible if (!string_or_null.is_null() && (!JavaObjectsInPerm || string_or_null()->is_perm())) { string = string_or_null; } else { string = java_lang_String::create_tenured_from_unicode(name, len, CHECK_NULL); } // Allocation must be done before grapping the SymbolTable_lock lock MutexLocker ml(StringTable_lock, THREAD); assert(java_lang_String::equals(string(), name, len), "string must be properly initialized"); // Since look-up was done lock-free, we need to check if another // thread beat us in the race to insert the symbol. oop test = lookup(index, name, len, hashValue); // calls lookup(u1*, int) if (test != NULL) { // Entry already added return test; } HashtableEntry<oop>* entry = new_entry(hashValue, string()); add_entry(index, entry); return string(); }
basic_add方法中的条件判断!string_or_null.is_null()
为true,!JavaObjectsInPerm
为true,所以并不会进行字符串的复制,而是通过HashtableEntry对象封装原字符串的hash值和指向源字符串的句柄,添加到StringTable对应bucket的链表中,并返回指向原字符串句柄。其中变量JavaObjectsInPerm默认为false,查看openjdk\hotspot\src\share\vm\runtime\globals.hpp文件:
develop(bool, JavaObjectsInPerm, false, \ "controls whether Classes and interned Strings are allocated" \ "in perm. This purely intended to allow debugging issues" \ "in production.")
大概含义是,控制Class实例和全局字符串常量池是否存储在永久代,默认值为false,表示会存储在java heap上。