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

打印 上一主题 下一主题

主题 966|帖子 966|积分 2898

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
##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_lookup 和 do_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++源码
##字符串表查找字符串
  1. JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
  2.   JvmtiVMObjectAllocEventCollector oam;
  3.   if (str == NULL) return NULL;
  4.   oop string = JNIHandles::resolve_non_null(str);
  5.   oop result = StringTable::intern(string, CHECK_NULL);
  6.   return (jstring) JNIHandles::make_local(THREAD, result);
  7. JVM_END
复制代码
 ##
  1. oop StringTable::intern(oop string, TRAPS) {
  2.   if (string == NULL) return NULL;
  3.   ResourceMark rm(THREAD);
  4.   int length;
  5.   Handle h_string (THREAD, string);
  6.   jchar* chars = java_lang_String::as_unicode_string(string, length,
  7.                                                      CHECK_NULL);
  8.   oop result = intern(h_string, chars, length, CHECK_NULL);
  9.   return result;
  10. }
复制代码
##
  1. oop StringTable::intern(Handle string_or_null_h, const jchar* name, int len, TRAPS) {
  2.   // shared table always uses java_lang_String::hash_code
  3.   unsigned int hash = java_lang_String::hash_code(name, len);
  4.   oop found_string = lookup_shared(name, len, hash);
  5.   if (found_string != NULL) {
  6.     return found_string;
  7.   }
  8.   if (_alt_hash) {
  9.     hash = hash_string(name, len, true);
  10.   }
  11.   found_string = do_lookup(name, len, hash);
  12.   if (found_string != NULL) {
  13.     return found_string;
  14.   }
  15.   return do_intern(string_or_null_h, name, len, hash, THREAD);
  16. }
复制代码
##从本地表查找
  1. oop StringTable::do_lookup(const jchar* name, int len, uintx hash) {
  2.   Thread* thread = Thread::current();
  3.   StringTableLookupJchar lookup(thread, hash, name, len);
  4.   StringTableGet stg(thread);
  5.   bool rehash_warning;
  6.   _local_table->get(thread, lookup, stg, &rehash_warning);
  7.   update_needs_rehash(rehash_warning);
  8.   return stg.get_res_oop();
  9. }
复制代码
##在本地表没有找到,则创建一个新的插入到本地表
  1. oop StringTable::do_intern(Handle string_or_null_h, const jchar* name,
  2.                            int len, uintx hash, TRAPS) {
  3.   HandleMark hm(THREAD);  // cleanup strings created
  4.   Handle string_h;
  5.   if (!string_or_null_h.is_null()) {
  6.     string_h = string_or_null_h;
  7.   } else {
  8.     string_h = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
  9.   }
  10.   assert(java_lang_String::equals(string_h(), name, len),
  11.          "string must be properly initialized");
  12.   assert(len == java_lang_String::length(string_h()), "Must be same length");
  13.   // Notify deduplication support that the string is being interned.  A string
  14.   // must never be deduplicated after it has been interned.  Doing so interferes
  15.   // with compiler optimizations done on e.g. interned string literals.
  16.   if (StringDedup::is_enabled()) {
  17.     StringDedup::notify_intern(string_h());
  18.   }
  19.   StringTableLookupOop lookup(THREAD, hash, string_h);
  20.   StringTableGet stg(THREAD);
  21.   bool rehash_warning;
  22.   do {
  23.     // Callers have already looked up the String using the jchar* name, so just go to add.
  24.     WeakHandle wh(_oop_storage, string_h);
  25.     // The hash table takes ownership of the WeakHandle, even if it's not inserted.
  26.     if (_local_table->insert(THREAD, lookup, wh, &rehash_warning)) {
  27.       update_needs_rehash(rehash_warning);
  28.       return wh.resolve();
  29.     }
  30.     // In case another thread did a concurrent add, return value already in the table.
  31.     // This could fail if the String got gc'ed concurrently, so loop back until success.
  32.     if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) {
  33.       update_needs_rehash(rehash_warning);
  34.       return stg.get_res_oop();
  35.     }
  36.   } while(true);
  37. }
复制代码
##jvm在初始化的时候,调用create_table生成本地表。
  1. if (UseSharedSpaces) {
  2.     // Read the data structures supporting the shared spaces (shared
  3.     // system dictionary, symbol table, etc.).  After that, access to
  4.     // the file (other than the mapped regions) is no longer needed, and
  5.     // the file is closed. Closing the file does not affect the
  6.     // currently mapped regions.
  7.     MetaspaceShared::initialize_shared_spaces();
  8.     StringTable::create_table();
  9.   } else
  10. #endif
  11.   {
  12.     SymbolTable::create_table();
  13.     StringTable::create_table();
  14.   }
复制代码
  1. void StringTable::create_table() {
  2.   size_t start_size_log_2 = ceil_log2(StringTableSize);
  3.   _current_size = ((size_t)1) << start_size_log_2;
  4.   log_trace(stringtable)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")",
  5.                          _current_size, start_size_log_2);
  6.   _local_table = new StringTableHash(start_size_log_2, END_SIZE, REHASH_LEN);
  7.   _oop_storage = OopStorageSet::create_weak("StringTable Weak", mtSymbol);
  8.   _oop_storage->register_num_dead_callback(&gc_notification);
  9. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦见你的名字

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表