在Java里字符串常量池如何工作_Java字符串池原理解析

Java字符串常量池是堆内存中的哈希表,用于缓存字面量和intern()字符串以节省内存;字面量自动入池,new String()总新建堆对象,intern()手动归池,JDK 1.7+后池位于堆中并受GC管理。

Java字符串常量池(String Constant Pool)本质上是一个**堆内存中的哈希表结构**,专门用于缓存字符串字面量和显式调用 intern() 的字符串对象,目的是避免重复创建内容相同的字符串,提升性能、节省内存。

字符串字面量自动入池

当你写 String s = "abc"; 时,JVM不会直接在堆中新建对象。它会:

  • 先查字符串常量池中是否已有内容为 "abc" 的字符串引用;
  • 如果有,直接返回该引用(即指向池中已存在的对象);
  • 如果没有,则在堆中创建一个新的 String 对象,并将它的引用存入常量池,再返回这个引用。

所以 String a = "abc"; String b = "abc"; 中,a == btrue —— 它们指向同一个堆对象。

new String() 总是在堆中新建对象

使用 new String("abc") 时,无论池中是否存在相同内容:

  • JVM一定在堆中创建一个新对象;
  • 但仍然会检查常量池:若池中无 "abc",则先将字面量 "abc" 加入池(注意:只是把字面量本身入池,不是 new 出来的那个对象);
  • 最终返回的是堆中新对象的引用,与池中对象无关。

因此 String a = "abc"; String b = new String("abc"); 中,a == bfalse,尽管内容相同。

intern() 方法是手动“归池”的关键

intern() 的作用是:确保当前字符串(无论怎么创建)在常量池中有且仅有一个对应引用。

  • 调用 s.intern() 时,JVM检查池中是否有内容相同的字符串引用;
  • 有,则直接返回池中那个引用;
  • 没有,则将当前字符串对象(或其内容)加入池,并返回该引用。

注意:JDK 1.7+ 后,intern() 不再复制字符串,而是把堆中已存在的字符串对象引用存入池中(池本身在堆里),所以效率更高、更省内存。

常量池位置随JDK版本变化

这个细节影响GC和内存调优:

  • JDK 1.6 及以前:字符串常量池在方法区(永久代);
  • JDK 1.7:移到堆中(但池中存的是堆对象的引用);
  • JDK 1.8+:永久代被元空间(Metaspace)取代,而字符串常量池始终保留在堆中。

这意味着从 JDK 1.7 起,字符串常量池的大小可由 -XX:MaxHeapSize 影响,也受 GC 管理——当堆内存紧张时,未被引用的池中字符串可能被回收(前提是无强引用指向它)。