优秀的编程知识分享平台

网站首页 > 技术文章 正文

016:字符串对象在JVM中是如何存放的

nanyue 2024-09-23 10:32:33 技术文章 4 ℃

本文首发于公众号:javaadu

典型答案

字符串对象在JVM中可能有两个存放的位置:字符串常量池或堆内存。

  • 使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中
  • 使用字符串构造方法创建的字符串对象,它的值存放在堆内存中

String提供了一个API——java.lang.String.intern(),这个API可以手动将一个字符串对象的值转移到字符串常量池中。

在1.7之前,字符串常量池是在PermGen区域,这个区域的大小是固定的——不能在运行时根据需要扩大,也不能被垃圾收集器回收,因此如果程序中有太多的字符串调用了intern方法的话,就可能造成OOM。

在1.7以后,字符串常量池移到了堆内存中,并且可以被垃圾收集器回收,这个改动降低了字符串常量池OOM的风险。

知识点总结

案例分析



String s1 = "javaadu";
String s2 = "javaadu";
String s3 = new String("javaadu");
 
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //false
String s4 = s3.intern();
System.out.println(s1 == s4); //true

intern方法源码分析

intern方法的实现底层是一个native方法,在Hotspot JVM里字符串常量池它的逻辑在注释里写得很清楚:如果常量池中有这个字符串常量,就直接返回,否则将

该字符串对象的值存入常量池,再返回。



这里以openjdk 1.8的源码为例,跟下intern方法的底层实现,String.java文件对应的C文件是String.c:

JNIEXPORT jobject JNICALL
Java_java_lang_String_intern(JNIEnv *env, jobject this)
{
 return JVM_InternString(env, this);
}

JVM_InternString这个方法的定义在jvm.h,实现在jvm.cpp中,在JVM中,Java世界和C++世界的连接层就是jvm.h和jvm.cpp这两文件。

// String support ///////////////////////////////////////////////////////////////////////////
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
 JVMWrapper("JVM_InternString");
 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(env, result);
JVM_END

可以看出,字符串常量池在JVM内部就是一个HashTable,也就是上面代码中的StringTable。

根据StringTable::intern方法跟下去,就可以跟到下面这段代码中,如果找到了就直接返回found_string,如果没有找到,就将当前的字符串加入到HashTable中,然后再返回。

oop StringTable::intern(Handle string_or_null, jchar* name,
 int len, TRAPS) {
 unsigned int hashValue = hash_string(name, len);
 int index = the_table()->hash_to_index(hashValue);
 oop found_string = the_table()->lookup(index, name, len, hashValue);
 // Found
 if (found_string != NULL) {
 ensure_string_alive(found_string);
 return found_string;
 }
 
 // …… ……
 Handle string;
 // try to reuse the string if possible
 if (!string_or_null.is_null()) {
 string = string_or_null;
 } else {
 string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
 }
 
 //…… ……
 // Grab the StringTable_lock before getting the_table() because it could change at safepoint.
 oop added_or_found;
 {
 MutexLocker ml(StringTable_lock, THREAD);
 // Otherwise, add to symbol to table
 added_or_found = the_table()->basic_add(index, string, name, len,
 hashValue, CHECK_NULL);
 }

相关问题

015:为什么Java中的字符串对象是不可变的

参考资料

  1. https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
  2. https://www.journaldev.com/797/what-is-java-string-pool
  3. https://www.baeldung.com/java-string-pool

本号(javaadu)专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

最近发表
标签列表