登录
首页 >  文章 >  java教程

怎么理解字符串常量池对相同字面量对象重用的底层原理

时间:2026-05-05 11:51:49 151浏览 收藏

欢迎各位小伙伴来到golang学习网,相聚于此都是缘哈哈哈!今天我给大家带来《怎么理解字符串常量池对相同字面量对象重用的底层原理》,这篇文章主要讲到等等知识,如果你对文章相关的知识非常感兴趣或者正在自学,都可以关注我,我会持续更新相关文章!当然,有什么建议也欢迎在评论留言提出!一起学习!

字符串常量池是堆中固定大小的StringTable哈希表(JDK1.8+默认容量60013),存储堆中字符串对象的引用;双引号创建时通过哈希定位、equals比对实现复用,new String()强制新建堆对象且不自动复用,运行时拼接字符串需显式调用intern()才可能入池。

怎么理解字符串常量池对相同字面量对象重用的底层原理

字符串常量池本质是堆里的 StringTable 哈希表

它不是魔法,而是一个固定大小的 HashTable(JDK1.7+),默认容量 60013(JDK1.8+),存储的是 堆中字符串对象的引用,不是字符串本身。key 是字符串内容(char[]byte[] 的哈希值),value 是该字符串在堆中的地址引用。

所以“重用”不是复制对象,而是让多个变量指向堆里同一个 String 实例——前提是它们都通过双引号字面量创建,且内容完全一致。

双引号赋值时 JVM 怎么查表并复用

执行 String s = "abc" 时,JVM 不会直接 new 对象,而是走以下流程:

  • 计算 "abc" 的哈希值,定位到 StringTable 对应桶(bucket)
  • 遍历该桶下的链表/红黑树,用 equals() 逐个比对内容(不是只比哈希值)
  • 命中则直接返回已有对象的引用;未命中则在堆中新建 String,再把其引用存入表中

这个过程在类加载的 ldc 指令阶段完成,属于运行时常量池解析的一部分,速度接近 O(1)。

new String("abc") 为什么不能自动复用

new 强制在堆上分配新对象,和常量池无关:

  • 先在堆创建一个全新 String 实例(即使内容相同)
  • 再检查常量池:若无 "abc",会把字面量 "abc" 加入池中(但不改变刚 new 出来的对象)
  • 最终返回的是堆对象引用,不是常量池里那个——所以 new String("abc") == "abc"false

注意:new String("abc").intern() 在 JDK1.7+ 会返回常量池中已有的引用(即和 "abc" 相同),但这是显式调用,不是自动行为。

容易被忽略的关键点:常量池只管字面量,不管运行时拼接

String s = "ab" + "c" 这种编译期可确定的拼接,会被 javac 优化成 "abc",走常量池;但 String s = "ab" + getStr()new StringBuilder().append("ab").toString() 生成的字符串,哪怕内容相同,也不会自动进常量池,除非手动调用 intern()

另外,StringTable 大小有限(默认 60013),冲突多或字符串过多时会发生哈希碰撞,影响查找效率;可通过 -XX:StringTableSize=120029 调整,但不能动态扩容。

今天关于《怎么理解字符串常量池对相同字面量对象重用的底层原理》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>