写点什么

字符串常量池 -StringTable 源码实现 2/3

作者:储诚益
  • 2025-01-28
    安徽
  • 本文字数:2052 字

    阅读完需:约 7 分钟

StringTable 是 JVM 中用于存储字符串常量的数据结构,它是 java.lang.String 对象的重要实现部分。StringTable 的主要作用是实现字符串的驻留(intern),即确保相同的字符串常量在内存中只存在一份,从而节省内存并提高性能。

1. StringTable 的基本概念

StringTable 是一个全局的哈希表,用于存储所有通过 String.intern() 方法驻留的字符串。JVM 在加载类时,会将类文件中的字符串常量(如 "hello")自动放入 StringTable 中。如果后续有相同的字符串常量出现,JVM 会直接从 StringTable 中返回已存在的字符串对象,而不是创建一个新的对象。

2. StringTable 的实现原理

StringTable 的实现依赖于 JVM 的具体实现。在 HotSpot JVM 中,StringTable 是一个基于 Hashtable 的数据结构。以下是 StringTable 的关键实现细节:

class StringTable : public Hashtable<oop, mtSymbol> {private:  // 静态实例,表示全局的字符串常量池  static StringTable* _the_table;
// 查找字符串 oop lookup(int index, jchar* name, int len, uintx hash);
// 插入字符串 oop basic_add(int index, Handle string_or_null, jchar* name, int len, uintx hash, TRAPS);
public: // 获取全局的字符串常量池实例 static StringTable* the_table() { return _the_table; }
// 查找字符串 static oop lookup(jchar* name, int len);
// 插入字符串 static oop intern(Handle string_or_null, jchar* name, int len, TRAPS);
// 其他方法...};
复制代码


StringTable 的初始化

StringTable 的初始化发生在 JVM 启动的早期阶段,在 Universe 模块初始化之后。Universe 是 JVM 中用于管理全局对象和元数据的基础模块,而 StringTable 作为全局的字符串常量池,需要在类加载和字符串常量解析之前完成初始化。

void Universe::initialize_heap() {  // 初始化堆内存  // ...  // 创建 StringTable  StringTable::create_table();  // ...}
复制代码

在 StringTable::create_table() 方法中,StringTable 的底层哈希表会被初始化。哈希表的大小可以通过 JVM 参数 -XX:StringTableSize=<size> 来指定,默认值通常是 60013(一个质数,有利于减少哈希冲突)。

void StringTable::create_table() {  size_t table_size = StringTableSize; // 从 JVM 参数中获取大小  _the_table = new StringTable(table_size);}
复制代码

StringTable 的底层实现是一个哈希表,其初始化过程包括分配内存和设置哈希表的基本属性(如桶的数量、哈希函数等)。

StringTable::StringTable(size_t table_size) : Hashtable<oop, mtSymbol>(table_size) {  // 初始化哈希表  initialize_table(table_size);}
复制代码

在 initialize_table() 方法中,哈希表的桶数组会被分配内存,并初始化为空链表。

void Hashtable<F, mtSymbol>::initialize_table(size_t table_size) {  _table_size = table_size;  _buckets = NEW_C_HEAP_ARRAY(HashtableEntry<F, mtSymbol>*, table_size, mtSymbol);  for (size_t i = 0; i < table_size; i++) {    _buckets[i] = NULL; // 初始化每个桶为空  }}
复制代码

lookup 方法

lookup 方法用于在字符串常量池中查找指定内容的字符串。

oop StringTable::lookup(jchar* name, int len) {  // 计算哈希值  uintx hash = java_lang_String::hash_code(name, len);  // 计算哈希桶的索引  int index = the_table()->hash_to_index(hash);  // 在哈希桶中查找字符串  return the_table()->lookup(index, name, len, hash);}
复制代码

intern 方法

intern 方法用于将字符串添加到字符串常量池中。

oop StringTable::intern(Handle string_or_null, jchar* name, int len, TRAPS) {  // 计算哈希值  uintx hash = java_lang_String::hash_code(name, len);  // 计算哈希桶的索引  int index = the_table()->hash_to_index(hash);  // 查找字符串  oop found_string = the_table()->lookup(index, name, len, hash);  if (found_string != nullptr) {    return found_string; // 如果已存在,则返回池中的字符串  }  // 如果不存在,则将字符串添加到常量池中  return the_table()->basic_add(index, string_or_null, name, len, hash, THREAD);}
复制代码

basic_add 方法

basic_add 方法用于将字符串插入到哈希表中。

oop StringTable::basic_add(int index, Handle string_or_null, jchar* name, int len, uintx hash, TRAPS) {  // 创建新的字符串对象  oop string = java_lang_String::create_oop_from_unicode(name, len, CHECK_NULL);  // 将字符串插入到哈希表中  the_table()->add_entry(index, string);  return string;}
复制代码

stringTable 的哈希算法

StringTable 的哈希值的计算由 java_lang_String::hash_code 方法实现。

uintx java_lang_String::hash_code(jchar* name, int len) {  uintx hash = 0;  for (int i = 0; i < len; i++) {    hash = 31 * hash + name[i];  }  return hash;}
复制代码


用户头像

储诚益

关注

还未添加个人签名 2017-12-19 加入

还未添加个人简介

评论

发布
暂无评论
字符串常量池-StringTable源码实现2/3_储诚益_InfoQ写作社区