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