首页 / 资讯中心 / 文章详情

Log4j2异步日志引发FullGC问题的排查与优化

Log4j2异步日志引发FullGC问题的排查与优化 ★ FEATURED ARTICLE
1. 问题现象与初步排查那天下午监控系统突然告警线上某核心服务的FullGC频率从平时的每天1-2次飙升到每小时3-4次。作为负责这个服务的老司机我第一反应是这不对劲。服务流量并没有明显增长代码最近一周也没发布变更但JVM的老年代就像漏水的桶一样内存哗哗地往下掉。先用标准三板斧开始排查立即dump了现场堆内存命令jmap -dump:formatb,fileheap.hprof pid抓取了GC日志启动参数需提前配置-XX:PrintGCDetails -XX:PrintGCDateStamps通过jstat -gcutil pid 1000实时观察各区内存变化2. 内存分析揪出真凶用MAT分析heap dump文件时发现一个反常现象char[]数组占用了近70%的老年代空间而正常情况下这个比例应该不到20%。展开引用链后发现这些字符数据全都来自日志系统的缓冲区。进一步检查Log4j2配置我们用的2.14.1版本AsyncLogger namecom.xxx.service levelinfo AppenderRef refKafkaAppender/ /AsyncLogger问题就出在这个看似无害的异步日志配置上。当Kafka集群网络出现波动时Appender的队列会持续堆积日志事件而Log4j2的异步日志器有个隐藏特性为了防止OOM它会将队列中的日志事件对象及其包含的char[]数组全部晋升到老年代3. 问题根因深度解析这个反直觉的行为其实在Log4j2官方文档中有提及但藏得很深异步日志默认使用ArrayBlockingQueue其特点是预分配固定大小数组当日志量超过队列大小时生产者线程会被阻塞为避免阻塞业务线程Log4j2会启动应急模式emergency mode在应急模式下新产生的日志事件会直接分配在老年代绕过新生代不会被GC回收直到被成功写入Appender我们服务的KafkaAppender因为网络问题写入缓慢导致持续处于应急模式。更糟的是日志事件中的char[]存储日志内容会随着业务请求不断创建最终撑爆老年代。4. 解决方案与验证经过测试我们采用组合方案4.1 短期应急措施!-- 添加discardThreshold防止队列积压 -- AsyncLogger namecom.xxx.service levelinfo includeLocationfalse AppenderRef refKafkaAppender/ RingBufferSize256/RingBufferSize DiscardThreshold10/DiscardThreshold /AsyncLogger关键参数说明RingBufferSize从默认的256*1024调整为256设置DiscardThreshold10当日志堆积超过90%时开始丢弃新日志关闭includeLocation获取代码位置会产生额外开销4.2 长期优化方案将KafkaAppender替换为异步本地文件双写模式增加日志分级熔断// 在关键业务日志处添加流控判断 if(Logger.getRootLogger().getAsyncLogger().getQueueSize() 1000) { metrics.counter(log_discard).increment(); return; }4.3 参数调优对比配置项原值调整后值影响RingBufferSize262144256降低内存占用DiscardThresholdInteger.MAX10防止内存爆炸includeLocationtruefalse减少60%的char[]分配GC策略ParallelG1更好处理大对象调整后FullGC频率降回每天0-1次期间即使Kafka集群再次出现波动服务也保持了稳定。5. 经验总结与避坑指南异步日志的隐藏成本实测显示异步日志在高压下会产生3-5倍的临时对象建议对异步Logger单独设置更小的队列大小不超过1024监控关键指标# 监控日志队列积压 jcmd pid Log4j2.status | grep AsyncAppender.*size # GC日志必加参数 -XX:PrintGCApplicationStoppedTime -XX:PrintPromotionFailure其他日志框架的对比Logback也有类似问题但默认使用链表结构队列Log4j2性能更好但内存风险更高终极方案考虑使用机械硬盘友好的写入策略这次事故给我的最大教训是任何异步组件都可能成为内存炸弹特别是在依赖外部系统如Kafka、DB的场景下。现在我们在所有异步Appender上都强制设置了DiscardThreshold并在Grafana上增加了日志队列的监控大盘。
阅读完成 · 觉得有帮助?
咨询建站