JVM字符串常量池位置、创建流程、对象个数与 intern字符串常量池是什么解决的是什么问题常量池的位置JDK 6/7/8 的常见理解先搞懂两种“创建字符串”的差别字面量直接走常量池复用new String(abc)堆上再来一份常见写法到底创建几个字符串对象同样字面量多次出现new String(123) 出现多次new String(a b)a bstr1 str2运行期拼接默认不会进常量池new String(ab) ab;new String(ab) new String(ab)new String(ab) new String(cd);intern()让“等值字符串”指向池里的那一个参考在 Java 里String无处不在JSON、SQL、日志、配置……字符串创建频繁如果每次都分配新对象会带来明显的内存与 GC 压力。于是 JVM/类加载体系为字符串字面量提供了一个“复用池”——字符串常量池String Intern Pool。字符串常量池是什么解决的是什么问题可以把字符串常量池理解为对“可复用字符串”的缓存表。当 class 文件里出现字符串字面量例如abcJVM 在加载/运行过程中会确保池里有一份对应的字符串对象后续再次用到同样字面量时直接复用池中的那一个引用避免重复分配。它能成立有一个关键前提String不可变。一旦某个abc被多个地方复用也不会出现“你改了我也跟着变”的数据竞争问题。常量池的位置JDK 6/7/8 的常见理解很多资料会把“方法区/永久代/元空间/堆”混在一起说。这里给一个更实用的记忆方式以 HotSpot 为主JDK 6字符串常量池常被认为在“永久代”方法区的一种实现里JDK 7 起字符串常量池迁移到堆更便于 GC、避免永久代容易 OOMJDK 8 起永久代移除类元数据转到元空间本地内存但字符串常量池仍在堆这一点延续结论现代 JavaJDK 8/11/17/21…里 基本可以按“字符串常量池在堆上”来理解。参考原文也按这个脉络展开。https://www.cnblogs.com/Andya/p/14067618.html先搞懂两种“创建字符串”的差别字面量直接走常量池复用Stringaabc;Stringbabc;System.out.println(ab);// true同一份池对象因为abc是字面量a和b指向同一个池中对象。new String(“abc”)堆上再来一份Stringaabc;StringbnewString(abc);System.out.println(ab);// falseSystem.out.println(a.equals(b));// true这里至少涉及两层含义abc这个字面量在池里如果之前没有则放进去new String(abc)堆上再 new 一个新对象内容来自池中的abc常见写法到底创建几个字符串对象同样字面量多次出现Strings1123;Strings2123;Strings3123;池里1 个123堆上0个没有new String/ 运行期拼接new String(“123”) 出现多次Strings4newString(123);Strings5newString(123);池里1 个123堆上2个new String(123)new String(123)new String(“a” “b”)Strings6newString(ab);new String(a b)在编译期会把a b常量折叠成ab等价于new String(ab)池里1 个ab堆上1 个new String(ab)“a” “b”Strings7ab;a b是编译期常量表达式会被常量折叠为ab等价于new String(ab)池1 个ab堆0 个没有new String/ 运行期拼接str1 str2运行期拼接默认不会进常量池Strings8ab;Strings9cd;Strings10s8s9;池2 个ab、cd堆1 个运行期拼接结果str3内容为abcd默认不进池【补充】运行期拼接过程中还会在堆上临时创建1 个StringBuilder对象以及其内部数组等但它不属于“字符串常量池”。它会在toString()时创建新的结果String对象例如abcd。new String(“ab”) “ab”;Strings11newString(ab)ab;池1 个ab堆2 个new String(ab)1 个 运行期拼接结果abab1 个new String(“ab”) new String(“ab”)池1 个ab堆3 个两个new String(ab) 运行期拼接结果ababnew String(“ab”) new String(“cd”);池2 个ab、cd堆3 个new String(ab)new String(cd) 运行期拼接结果abcdintern()让“等值字符串”指向池里的那一个StringxnewString(123);// 堆对象Stringyx.intern();// 返回池中对应对象的引用System.out.println(y123);// true理解一句话就够了intern()返回“常量池中等内容字符串”的引用如果池里没有可能把当前字符串或其等价表示放进去再返回池引用。参考原文https://www.cnblogs.com/Andya/p/14067618.html
【Java SE】JVM字符串常量池:位置、创建流程、对象个数与 `intern()`
发布时间:2026/5/26 16:52:27
JVM字符串常量池位置、创建流程、对象个数与 intern字符串常量池是什么解决的是什么问题常量池的位置JDK 6/7/8 的常见理解先搞懂两种“创建字符串”的差别字面量直接走常量池复用new String(abc)堆上再来一份常见写法到底创建几个字符串对象同样字面量多次出现new String(123) 出现多次new String(a b)a bstr1 str2运行期拼接默认不会进常量池new String(ab) ab;new String(ab) new String(ab)new String(ab) new String(cd);intern()让“等值字符串”指向池里的那一个参考在 Java 里String无处不在JSON、SQL、日志、配置……字符串创建频繁如果每次都分配新对象会带来明显的内存与 GC 压力。于是 JVM/类加载体系为字符串字面量提供了一个“复用池”——字符串常量池String Intern Pool。字符串常量池是什么解决的是什么问题可以把字符串常量池理解为对“可复用字符串”的缓存表。当 class 文件里出现字符串字面量例如abcJVM 在加载/运行过程中会确保池里有一份对应的字符串对象后续再次用到同样字面量时直接复用池中的那一个引用避免重复分配。它能成立有一个关键前提String不可变。一旦某个abc被多个地方复用也不会出现“你改了我也跟着变”的数据竞争问题。常量池的位置JDK 6/7/8 的常见理解很多资料会把“方法区/永久代/元空间/堆”混在一起说。这里给一个更实用的记忆方式以 HotSpot 为主JDK 6字符串常量池常被认为在“永久代”方法区的一种实现里JDK 7 起字符串常量池迁移到堆更便于 GC、避免永久代容易 OOMJDK 8 起永久代移除类元数据转到元空间本地内存但字符串常量池仍在堆这一点延续结论现代 JavaJDK 8/11/17/21…里 基本可以按“字符串常量池在堆上”来理解。参考原文也按这个脉络展开。https://www.cnblogs.com/Andya/p/14067618.html先搞懂两种“创建字符串”的差别字面量直接走常量池复用Stringaabc;Stringbabc;System.out.println(ab);// true同一份池对象因为abc是字面量a和b指向同一个池中对象。new String(“abc”)堆上再来一份Stringaabc;StringbnewString(abc);System.out.println(ab);// falseSystem.out.println(a.equals(b));// true这里至少涉及两层含义abc这个字面量在池里如果之前没有则放进去new String(abc)堆上再 new 一个新对象内容来自池中的abc常见写法到底创建几个字符串对象同样字面量多次出现Strings1123;Strings2123;Strings3123;池里1 个123堆上0个没有new String/ 运行期拼接new String(“123”) 出现多次Strings4newString(123);Strings5newString(123);池里1 个123堆上2个new String(123)new String(123)new String(“a” “b”)Strings6newString(ab);new String(a b)在编译期会把a b常量折叠成ab等价于new String(ab)池里1 个ab堆上1 个new String(ab)“a” “b”Strings7ab;a b是编译期常量表达式会被常量折叠为ab等价于new String(ab)池1 个ab堆0 个没有new String/ 运行期拼接str1 str2运行期拼接默认不会进常量池Strings8ab;Strings9cd;Strings10s8s9;池2 个ab、cd堆1 个运行期拼接结果str3内容为abcd默认不进池【补充】运行期拼接过程中还会在堆上临时创建1 个StringBuilder对象以及其内部数组等但它不属于“字符串常量池”。它会在toString()时创建新的结果String对象例如abcd。new String(“ab”) “ab”;Strings11newString(ab)ab;池1 个ab堆2 个new String(ab)1 个 运行期拼接结果abab1 个new String(“ab”) new String(“ab”)池1 个ab堆3 个两个new String(ab) 运行期拼接结果ababnew String(“ab”) new String(“cd”);池2 个ab、cd堆3 个new String(ab)new String(cd) 运行期拼接结果abcdintern()让“等值字符串”指向池里的那一个StringxnewString(123);// 堆对象Stringyx.intern();// 返回池中对应对象的引用System.out.println(y123);// true理解一句话就够了intern()返回“常量池中等内容字符串”的引用如果池里没有可能把当前字符串或其等价表示放进去再返回池引用。参考原文https://www.cnblogs.com/Andya/p/14067618.html