不可变对象内存如何回收?Java字符串常量池等相关技术问询
Java字符串常量池与不可变类相关问题解答
1. 基础认知确认
你的陈述准确:从JDK 7开始,字符串常量池从永久代(PermGen)迁移至堆内存;垃圾回收器(GC)的核心职责就是回收堆内存中无引用的废弃对象。
2. GC是否不会清理字符串常量池?
这个说法并不成立,需结合JDK版本区分场景:
- JDK 6及更早版本:字符串常量池位于永久代,而永久代仅在Full GC时才会触发回收,且回收条件极为苛刻,容易让人产生“GC不清理常量池”的误解。
- JDK 7及以后版本:常量池移至堆内存后,只要常量池中的字符串对象没有被任何强引用指向,就会被GC(Young GC或Full GC均可)正常回收。比如通过
String.intern()放入常量池的对象,当外部所有指向它的引用都被销毁时,一样会被清理。
3. 字符串常量池的内存如何被清理?
常量池的内存清理遵循堆内存GC的通用规则,核心触发条件是对象失去所有强引用,具体场景包括:
- 当常量池中的字符串对象未被任何类、实例、变量持有强引用时,GC在执行标记-清除/标记-整理等流程时,会将其判定为可回收对象。
- 动态放入常量池的字符串(如调用
intern()的对象),若后续所有引用都被销毁,会和堆中普通对象一样被回收。 - 注意:JVM启动时加载的系统类所引用的字符串常量(如
"java.lang.Object"),只要系统类本身未被卸载,这些常量就不会被回收。
4. 如何识别Java中的不可变类,其对象存储在何处?
不可变类的识别特征
不可变类指对象创建后状态无法被修改的类,识别可参考以下几点:
- 类被声明为
final,防止被继承后篡改行为逻辑。 - 所有成员变量为
private final,禁止外部直接修改,且初始化后无法变更。 - 不提供修改成员变量的setter方法,内部也无任何会修改成员变量的逻辑。
- 若成员变量为引用类型,不会对外暴露该引用,且内部不会修改引用指向的对象(如返回集合时返回不可变副本)。
- Java内置典型不可变类:
String、所有包装类(Integer/Double等)、LocalDate/LocalTime等日期时间类。
不可变类对象的存储位置
不可变类对象的存储位置取决于创建方式,并非固定在常量池:
- 字面量创建的对象:如
String s = "abc"、Integer i = 50(在Integer缓存范围内),这类对象会存储在对应的常量池/缓存池(字符串常量池、IntegerCache)中,属于堆内存的一部分。 new关键字创建的对象:如String s = new String("abc")、Integer i = new Integer(50),这类对象直接存储在堆的普通区域(年轻代或老年代),同时可能在常量池中生成对应的字面量(若之前不存在)。- 动态生成的不可变对象:如
String s = new StringBuilder("x").append("y").toString(),这类对象默认存储在堆的普通区域,调用intern()后才会进入字符串常量池。
内容的提问来源于stack exchange,提问作者AuroraNightshade
相关产品推荐
相关产品推荐

