Java常量池是类文件核心组件,编译期存储字面量,运行期通过字符串常量池和包装类缓存实现对象复用,减少堆内存占用与GC压力。

Java常量池(Constant Pool)是类文件结构中的核心组件之一,它在编译期和运行期共同参与字面量的存储与复用,直接支撑字符串、基本类型包装类等对象的内存优化。这种优化不是靠“自动回收”,而是通过显式共享引用减少重复对象创建,从而降低堆内存占用和GC压力。
编译期:字面量进入class文件常量池
Java源码中出现的字符串字面量(如"hello")、整数字面量(如100)、类/方法名等,在编译时就被收集进class文件的常量池表(Constant Pool Table)。这个表是只读的静态结构,由索引定位,不占用运行时堆空间。
- 字符串字面量会以CONSTANT_String_info形式存入,并指向一个CONSTANT_Utf8_info项
- 整数范围[-128, 127]的Integer字面量(如int i = 100;)虽不直接进常量池,但其装箱结果受IntegerCache影响,本质是JVM对常用小整数的缓存策略,与常量池协同工作
- final static String s = "abc";这类编译期常量,会被内联进调用处,且对应字面量仍进入常量池
运行期:字符串常量池(String Table)实现字面量重用
类加载时,class文件常量池中的字符串字面量会被提取并存入JVM全局的字符串常量池(String Table),默认位于堆内存(JDK 7+)或元空间(JDK 7之前在永久代)。每次遇到字符串字面量,JVM会先查表,命中则复用已有引用,否则新建并加入池中。
- String a = "hello"; String b = "hello"; → a == b为true,因指向同一池中对象
- String c = new String("hello"); → 在堆上新建对象,c == a为false;但c.intern()可将其引用加入字符串池(若未存在),之后再调用intern()将返回池中引用
- 字符串池大小可通过-XX:StringTableSize调整,默认为60013,哈希冲突多时扩容可提升查找效率
基本类型包装类缓存:常量池思想的延伸应用
虽然Integer、Byte、Short、Long、Character的缓存机制不由class常量池直接管理,但设计逻辑高度一致:对高频小值预建对象并复用,避免重复分配。
立即学习“Java免费学习笔记(深入)”;
- Integer i1 = 100; Integer i2 = 100; → i1 == i2为true(默认缓存[-128, 127])
- Integer i3 = 200; Integer i4 = 200; → i3 == i4为false(超出缓存范围,各自new)
- 缓存范围可通过-XX:AutoBoxCacheMax参数扩大(仅对Integer有效)
注意边界:哪些不会被重用?
常量池优化有明确适用边界,理解这些限制才能避免误判内存行为:
- 运行期拼接的字符串(如"a" + "b"在编译期可确定,会被优化为字面量;但s1 + s2含变量则生成新StringBuilder对象)
- 通过Class.forName()、反射、动态代理等生成的类,其符号引用不参与当前类的常量池复用
- 不同类加载器加载的同名类,其常量池相互隔离,即使内容相同也不会共享
















