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;}
复制代码
评论