标记-清除算法不移动对象,导致内存碎片化,日志表现为promotion failed、concurrent mode failure及老年代释放空间远小于预期;标记-整理算法通过压缩存活对象消除碎片,日志中可见marksweepcompact字样、更长stw时间及gc后老年代使用率显著下降。

GC日志是观察垃圾回收行为最直接的窗口,而标记-清除(Mark-Sweep)和标记-整理(Mark-Compact)这两种算法在实际运行中会留下明显不同的日志特征。通过分析日志中的内存变化模式、停顿时间、碎片迹象和晋升行为,可以有效区分二者并评估其性能表现。
看日志里有没有“内存碎片化”的间接证据
标记-清除算法不移动对象,只清理未被标记的空间,容易导致堆内存碎片化。这种碎片在GC日志中不会直接写成“碎片”二字,但会体现为:
- 每次Minor GC后,老年代使用量缓慢上升,但Full GC触发前老年代已接近满,却仍无法分配大对象(如日志中出现
promotion failed或concurrent mode failure) - Full GC前后,老年代释放空间远小于预期(比如GC前占用98%,GC后仍剩75%,说明大量小块空闲但无法合并)
- 日志中频繁出现
CMS(Concurrent Mark Sweep)或Serial Old的标记-清除行为,且伴随Fragmentation相关警告(部分JVM版本会输出)
对比停顿时间和内存压缩痕迹
标记-整理算法会在回收阶段将存活对象向一端移动,因此会产生更长的STW(Stop-The-World)时间,但换来连续可用空间。日志中可识别:
- Full GC日志行中明确包含
MarkSweepCompact或PSMarkSweep(Parallel Scavenge + Parallel Old 组合)字样 - GC耗时明显高于同负载下的标记-清除型GC(例如同为Full GC,
PSMarkSweep耗时 320ms,而CMS回退到Serial Old仅 180ms,但后者随后很快再次触发) - GC后老年代使用率显著下降,且后续分配大对象不再失败(说明整理后产生了足够连续空间)
关注晋升与跨代引用带来的差异
标记-清除类收集器(如CMS)为降低停顿,采用并发标记,但需额外记录跨代引用(通过卡表 Card Table)。这会在日志中体现为:
- 大量
CMSCardTableModRefBS::process_stride或DirtyCardQueueSet相关调试信息(需开启-XX:+PrintGCDetails -Xlog:gc+ref=debug) - CMS周期中出现
Concurrent Mode Failure,紧接着 fallback 到 Serial Old 的标记-整理过程——此时日志会突然切换为长停顿、无并发阶段的完整标记→整理→清除流程
用关键指标横向比对
不必依赖单次日志,应统计一段时间内的规律性数据:
- 计算每次Full GC后老年代剩余空间的“最大连续空闲块占比”(需借助工具如 GCViewer 或自己解析
free字段变化趋势) - 统计单位时间内
promotion failure出现次数:高频即暗示标记-清除下碎片严重 - 对比相同堆大小下,启用
-XX:+UseParallelOldGC(默认标记-整理)与-XX:+UseConcMarkSweepGC(标记-清除为主)的日志吞吐量和GC频率
这些线索综合起来,就能判断当前GC行为更倾向哪种算法,并进一步评估其在具体业务负载下的实际开销与稳定性。










