java中string内存分配核心是字符串常量池:字面量直接入池复用,new强制堆中新建但参数仍触池,intern手动拉取或加入池;==比较仅适用于同池引用,需谨慎使用。

Java中String变量的内存分配,核心在于字符串常量池(String Pool)的行为机制。它不是抽象概念,而是真实影响对象创建、引用比较、GC压力和性能调优的关键区域。理解它,关键看三点:字面量怎么进池、new怎么绕池、intern怎么拉回池。
字面量赋值:编译期锁定,运行时查池复用
写 String s = "abc" 时,JVM在类加载阶段就把"abc"注册进字符串常量池(JDK7+在堆中)。后续所有相同字面量都复用同一对象。
- 编译期:该字符串已写入.class文件的常量池表;
- 类加载时:JVM将字面量加载到运行时常量池(堆内);
- 首次执行时:若池中无"abc",则在堆中新建并驻留;若有,则直接返回其引用。
所以 String a = "hello"; String b = "hello"; a == b 为 true —— 它们指向堆中同一个常量池对象。
new String("xxx"):堆中必新建,常量池仅被动参与
new 强制在堆中创建全新String实例,与常量池无关。但构造参数"xxx"本身是字面量,仍会触发常量池检查(可能新增或复用)。
- 先在堆中分配一个String对象(含value字段指向char[]/byte[]);
- 再检查常量池中是否存在"xxx":不存在则加入,存在则跳过;
- 该String对象的内部value数组,最终指向常量池中那个"xxx"的底层数据(JDK9+为byte[])。
因此 String x = new String("abc"); String y = "abc"; x == y 为 false,但 x.equals(y) 和 x.intern() == y 均为 true。
intern():手动触发“拉取”逻辑,决定是否复用
String.intern() 是显式干预常量池的唯一标准API。它的行为在JDK6/7/8有差异,当前(JDK8+)语义统一:
- 若常量池中已存在等于该字符串的实例,直接返回池中引用;
- 否则,将该字符串实例复制一份(注意:不是引用)加入常量池,并返回池中这个新副本的引用。
例如:
String s1 = new String("test").intern();
String s2 = "test";
s1 == s2 → true
但注意:如果常量池已有"test",new String("test").intern() 返回的就是池中原有引用,不会复制。
实战建议:避免误判、控制膨胀、关注GC
日常开发中容易踩坑的地方:
- 永远别用 == 比较两个String变量的值是否相等(除非你明确知道它们都来自字面量或已调用intern);
- 大量动态拼接字符串后反复调用 intern(),可能导致常量池哈希冲突加剧(默认大小1009),可用 -XX:StringTableSize=60013 调整;
- 字符串常量池位于堆中,其对象受GC管理;但池中对象只要被引用(包括被其他String.value引用),就不会被回收;
- 读取外部数据(如JSON、DB字段)生成String后立即intern,需评估重复率——低重复率反而增加内存开销。










