网站首页 > 技术文章 正文
本文首发于公众号: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); }
相关问题
参考资料
- https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
- https://www.journaldev.com/797/what-is-java-string-pool
- https://www.baeldung.com/java-string-pool
本号(javaadu)专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。
猜你喜欢
- 2024-09-23 Java虚拟机类加载机制及双亲委派模式分析
- 2024-09-23 阿里巴巴 Java 开发手册之MySQL 规约
- 2024-09-23 嵌入式C基础编程——5年程序员给你讲解数据类型、运算符与表达式
- 2024-09-23 从linux源码看socket(tcp)的timeout
- 2024-09-23 MySQL高性能优化规范建议,从设计,命名,开发等一条线的建议
- 2024-09-23 十五、Java运算符-赋值运算符与instanceof运算符
- 2024-09-23 面试官:MySQL的自增ID用完了,怎么办?
- 2024-09-23 Redis 数据结构与内存管理策略(上)
- 2024-09-23 用 C++ 和 Java 写算法,差别大吗?
- 2024-09-23 程序员必须了解的——契约式编程(面向契约编程)
- 1514℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 569℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 510℃MySQL service启动脚本浅析(r12笔记第59天)
- 486℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 485℃启用MySQL查询缓存(mysql8.0查询缓存)
- 467℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 446℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 444℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)