为何JVM存在大量未被回收的char数组与String对象?
JVM堆中非存活char数组/String对象相关问题解答
1. jmap -histo与jmap -histo:live的核心差异
jmap -histo:不会触发GC,直接统计堆内所有已分配内存的对象,包含已经失去引用、等待GC回收的垃圾对象,你看到的1000M+char数组就是所有已分配的char数组总大小。jmap -histo:live:执行前会主动触发一次Full GC,仅统计GC后仍然存活的、有引用链可达的对象,你看到的140M就是当前真正在用的char数组大小。
两者的差值860M就是已经死亡、等待被GC回收的char数组总大小,并不是凭空消失了。
2. 该情况是否正常、是否需要关注
- 绝大多数场景下属于正常现象,不需要特殊处理:JVM的GC本身是懒执行的,不会对象一失去引用就立刻回收,频繁GC反而会浪费CPU资源,只要GC频率、单次GC耗时符合业务预期,堆内存剩余充足,就无需干预。
- 需要关注排查的场景:
- 堆内存使用率长期居高不下,GC触发频繁、单次STW耗时过高,已经影响业务吞吐量或请求延迟
- 每次统计的非存活String/char数组大小持续暴涨,需要排查代码是否存在不必要的大批量字符串生成逻辑
3. 死亡对象未被回收的原因、回收时机
- 未回收原因:GC还未触发。不同GC回收器的触发条件不同:新生代内存占满会触发Young GC清理新生代垃圾,老年代占用达到对应阈值(如CMS的
-XX:CMSInitiatingOccupancyFraction设置值、G1的IHOP动态阈值)会触发Old GC/Full GC清理全堆垃圾,未到触发阈值时死亡对象会暂时占用内存。 - 回收时机:当对应内存区域的占用达到GC触发阈值时,GC执行过程中就会自动清理所有无引用的死亡对象,释放对应内存。
4. 手动触发GC释放内存的方法
如果你的JVM没有配置-XX:+DisableExplicitGC参数,可通过以下两种方式手动触发Full GC立即释放这部分内存:
- 直接执行
jmap -histo:live {pid},命令执行过程中就会自动触发Full GC - 执行
jcmd {pid} GC.run主动触发Full GC
注意:生产环境手动触发Full GC会产生STW停顿,请避开业务高峰执行,避免影响正常业务请求。如果配置了
-XX:+DisableExplicitGC参数,手动触发GC的命令会失效,只能等待JVM自动触发GC。
内容的提问来源于stack exchange,提问作者Zhang Buzz
相关产品推荐
相关产品推荐

