以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上。