欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > openjdk17 从C++源码视角 intern方法看字符串常量池

openjdk17 从C++源码视角 intern方法看字符串常量池

2024/12/22 0:10:32 来源:https://blog.csdn.net/u014200244/article/details/144477060  浏览:    关键词:openjdk17 从C++源码视角 intern方法看字符串常量池

##String native方法

public native String intern();

1. intern() 方法在 Java 中的作用

在 Java 中,String#intern() 方法是一个本地方法,用于确保字符串常量池中只有一个字符串的实例。当你调用 intern() 方法时,它会检查字符串常量池中是否已经存在一个相同的字符串。如果存在,它返回池中的字符串引用,否则将字符串添加到常量池中并返回该引用。

2. JVM_InternString C++ 实现

JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) 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(THREAD, result); JVM_END

这个 C++ 函数是 String#intern() 的本地实现。它首先获取 jstring 类型的字符串引用,然后通过 StringTable::intern 来查找或插入字符串到字符串常量池中。如果字符串已经存在,则返回池中的字符串引用,否则将其插入池中。

3. StringTable::intern 方法

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, CHECK_NULL); oop result = intern(h_string, chars, length, CHECK_NULL); return result; }

  • 这里的 intern 方法接受一个 oop(对象指针)类型的字符串,并将其转换为 Unicode 字符数组。然后它调用另一个重载的 intern 方法,实际执行字符串查找或插入操作。
  • ResourceMark 是为了确保在执行期间分配的资源能够在结束时被回收。

4. 字符串查找和插入 (do_lookupdo_intern)

StringTable::do_lookup 中,我们执行的是对字符串常量池的查找操作。它首先会尝试在本地表中查找该字符串。如果找到了,它就返回找到的字符串对象(found_string)。

oop StringTable::do_lookup(const jchar* name, int len, uintx hash) { Thread* thread = Thread::current(); StringTableLookupJchar lookup(thread, hash, name, len); StringTableGet stg(thread); bool rehash_warning; _local_table->get(thread, lookup, stg, &rehash_warning); update_needs_rehash(rehash_warning); return stg.get_res_oop(); }

  • 在本地表(_local_table)中查找字符串时,它会计算字符串的哈希值并查找对应的条目。
  • 如果查找到了字符串,stg.get_res_oop() 会返回字符串对象(oop)。

5. 字符串插入操作 (do_intern)

如果在查找时未找到该字符串,do_intern 会创建一个新的字符串对象并将其插入到常量池中。

oop StringTable::do_intern(Handle string_or_null_h, const jchar* name, int len, uintx hash, TRAPS) { HandleMark hm(THREAD); // cleanup strings created Handle string_h; if (!string_or_null_h.is_null()) { string_h = string_or_null_h; } else { string_h = java_lang_String::create_from_unicode(name, len, CHECK_NULL); } assert(java_lang_String::equals(string_h(), name, len), "string must be properly initialized"); assert(len == java_lang_String::length(string_h()), "Must be same length"); // Check for deduplication if (StringDedup::is_enabled()) { StringDedup::notify_intern(string_h()); } StringTableLookupOop lookup(THREAD, hash, string_h); StringTableGet stg(THREAD); bool rehash_warning; do { WeakHandle wh(_oop_storage, string_h); if (_local_table->insert(THREAD, lookup, wh, &rehash_warning)) { update_needs_rehash(rehash_warning); return wh.resolve(); } if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) { update_needs_rehash(rehash_warning); return stg.get_res_oop(); } } while (true); }

  • 创建字符串对象:如果传入的字符串为空,它会通过 create_from_unicode 创建一个新的字符串对象。
  • 插入字符串:然后通过 WeakHandle 将字符串插入到本地表中。
  • 检查重复:如果存在重复的字符串,它会通过循环保证字符串只有一个实例。如果另一个线程已经插入了相同的字符串,当前线程会发现并直接返回该字符串。

6. 字符串表的初始化 (create_table)

在 JVM 启动时,字符串常量池会通过 StringTable::create_table 方法来初始化本地表。

void StringTable::create_table() { size_t start_size_log_2 = ceil_log2(StringTableSize); _current_size = ((size_t)1) << start_size_log_2; log_trace(stringtable)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")", _current_size, start_size_log_2); _local_table = new StringTableHash(start_size_log_2, END_SIZE, REHASH_LEN); _oop_storage = OopStorageSet::create_weak("StringTable Weak", mtSymbol); _oop_storage->register_num_dead_callback(&gc_notification); }

  • StringTableSize:这是常量池的初始大小,它会根据配置进行调整。
  • StringTableHash:用于实现字符串常量池的哈希表。
  • OopStorageSet:用于存储字符串对象,WeakHandle 确保对象在垃圾回收时不会被误删除。

总结

C++源码中清晰地展示了如何通过 StringTable 管理字符串常量池。intern() 方法的核心逻辑包括查找、插入和确保字符串唯一性的操作。不同的函数(如 do_lookup, do_intern)和结构(如 WeakHandle, StringTableHash)确保了线程安全、性能和内存管理。

##C++源码

##字符串表查找字符串

JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))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(THREAD, result);
JVM_END

 ##

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,CHECK_NULL);oop result = intern(h_string, chars, length, CHECK_NULL);return result;
}

##

oop StringTable::intern(Handle string_or_null_h, const jchar* name, int len, TRAPS) {// shared table always uses java_lang_String::hash_codeunsigned int hash = java_lang_String::hash_code(name, len);oop found_string = lookup_shared(name, len, hash);if (found_string != NULL) {return found_string;}if (_alt_hash) {hash = hash_string(name, len, true);}found_string = do_lookup(name, len, hash);if (found_string != NULL) {return found_string;}return do_intern(string_or_null_h, name, len, hash, THREAD);
}

##从本地表查找

oop StringTable::do_lookup(const jchar* name, int len, uintx hash) {Thread* thread = Thread::current();StringTableLookupJchar lookup(thread, hash, name, len);StringTableGet stg(thread);bool rehash_warning;_local_table->get(thread, lookup, stg, &rehash_warning);update_needs_rehash(rehash_warning);return stg.get_res_oop();
}

##在本地表没有找到,则创建一个新的插入到本地表

oop StringTable::do_intern(Handle string_or_null_h, const jchar* name,int len, uintx hash, TRAPS) {HandleMark hm(THREAD);  // cleanup strings createdHandle string_h;if (!string_or_null_h.is_null()) {string_h = string_or_null_h;} else {string_h = java_lang_String::create_from_unicode(name, len, CHECK_NULL);}assert(java_lang_String::equals(string_h(), name, len),"string must be properly initialized");assert(len == java_lang_String::length(string_h()), "Must be same length");// Notify deduplication support that the string is being interned.  A string// must never be deduplicated after it has been interned.  Doing so interferes// with compiler optimizations done on e.g. interned string literals.if (StringDedup::is_enabled()) {StringDedup::notify_intern(string_h());}StringTableLookupOop lookup(THREAD, hash, string_h);StringTableGet stg(THREAD);bool rehash_warning;do {// Callers have already looked up the String using the jchar* name, so just go to add.WeakHandle wh(_oop_storage, string_h);// The hash table takes ownership of the WeakHandle, even if it's not inserted.if (_local_table->insert(THREAD, lookup, wh, &rehash_warning)) {update_needs_rehash(rehash_warning);return wh.resolve();}// In case another thread did a concurrent add, return value already in the table.// This could fail if the String got gc'ed concurrently, so loop back until success.if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) {update_needs_rehash(rehash_warning);return stg.get_res_oop();}} while(true);
}

##jvm在初始化的时候,调用create_table生成本地表。

if (UseSharedSpaces) {// Read the data structures supporting the shared spaces (shared// system dictionary, symbol table, etc.).  After that, access to// the file (other than the mapped regions) is no longer needed, and// the file is closed. Closing the file does not affect the// currently mapped regions.MetaspaceShared::initialize_shared_spaces();StringTable::create_table();} else
#endif{SymbolTable::create_table();StringTable::create_table();}
void StringTable::create_table() {size_t start_size_log_2 = ceil_log2(StringTableSize);_current_size = ((size_t)1) << start_size_log_2;log_trace(stringtable)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")",_current_size, start_size_log_2);_local_table = new StringTableHash(start_size_log_2, END_SIZE, REHASH_LEN);_oop_storage = OopStorageSet::create_weak("StringTable Weak", mtSymbol);_oop_storage->register_num_dead_callback(&gc_notification);
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com