JVM GC长暂停问题排查

发布于:2025-07-14 ⋅ 阅读:(19) ⋅ 点赞:(0)

JVM GC长暂停问题排查

现象

名词:GC 垃圾回收(Garbage Collection)分类 计算机科学
在高并发下,Java程序的GC问题属于很典型的一类问题,带来的影响往往会被进一步放大。不管是「GC频率过快」还是「GC耗时太长」,由于GC期间都存在Stop The World问题,因此很容易导致服务超时,引发性能问题。

事情最初是线上某应用垃圾收集出现Full GC异常的现象,应用中个别实例Full GC时间特别长,持续时间约为15~30秒,平均每2周左右触发一次;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

JVM参数配置“-Xms2048M –Xmx2048M –Xmn1024M –XX:MaxPermSize=512M”

  1. 周期性长暂停

    • 线上Java应用每2周触发1次Full GC
    • 单次暂停15-30秒(正常Full GC应≤1秒)
    • 仅部分实例出现,JVM配置一致(-Xms2048M –Xmx2048M –Xmn1024M –XX:MaxPermSize=512M
  2. 异常GC特征

    • Full GC原因标记为 Ergonomics(JVM自适应策略触发)
    • GC日志未显示堆内存异常(回收前后内存占比正常)

背景

  1. 系统环境

    • 部署于 Linux虚拟机(物理内存8GB)
    • 关键参数:vm.swappiness=30(默认倾向使用SWAP)
  2. 并发场景

    • 高QPS服务:持续内存分配压力
    • 内存使用特点:
      监控项 现象
      物理内存 未完全耗尽(free显示可用内存充足)
      SWAP分区 占用305MB(进程检测)
      CPU Full GC时骤增
  3. 矛盾点

    • JVM认为内存充足(堆配置仅2GB),但OS将部分内存页换出到SWAP磁盘
    • 低频率Full GC(2周1次)→ 内存页在SWAP停留时间长 → GC遍历时触发磁盘换入

核心冲突示意图

JVM Full GC  
│  
├─ 需遍历堆内存  
│     │  
│     ├─ 内存页在物理内存 → 微秒级访问  
│     │  
│     └─ 内存页在SWAP磁盘 → 毫秒级磁盘I/O(**10^3倍延迟**)  
│  
└─ 大量页换入操作阻塞GC线程 → **STW时间膨胀至秒级**  

关键结论:低频Full GC + SWAP换出 + 遍历换入需求 = 长暂停灾难链


第一层:核心概念定义
  1. JVM与GC机制

    • Java虚拟机(JVM)通过垃圾回收(GC)管理内存,GC执行时会触发"Stop The World"(STW)暂停
    • Full GC:清理整个堆内存的回收操作,耗时显著
  2. 问题现象

    • 某应用实例周期性出现15-30秒Full GC
    • JVM配置:-Xms2048M –Xmx2048M –Xmn1024M –XX:MaxPermSize=512M
    • Full GC诱因:Ergonomics(自适应策略触发)

第二层:系统级分析
  1. GC日志与服务器指标关联

    • GC日志显示回收前后堆内存无异常
    • 监控发现Full GC时点出现:
      • CPU使用率骤增(图1红框)
      • 物理内存增长拐点 + SWAP区释放(图1橙框)
  2. SWAP机制验证

    # 检测进程SWAP占用
    for i in $(cd /proc; ls | grep "^[0-9]" | awk '$0>100'); do 
      awk '/Swap/{a=a+$2} END{print '"$i"',a/1024"M"}' /proc/$i/smaps 2>/dev/null
    done | sort -k2nr | head -10
    
    • 目标进程占用SWAP 305MB
    • 异常实例swappiness=30(倾向使用SWAP),正常实例swappiness=0

第三层:根因分析
  1. SWAP与GC的致命交互

    • Linux内存压力时:物理内存页换出到SWAP(swap out)
    • Full GC遍历堆内存时:SWAP数据换回物理内存(swap in)
    • 磁盘I/O操作导致GC遍历耗时剧增(从毫秒级→秒级)
  2. 对比实验佐证

    场景 SWAP用量 Full GC耗时 关键差异
    问题实例(2周1次) 305MB 15-30秒 内存页被换出至SWAP
    实名服务(几小时1次) 54MB 576ms SWAP无活动+频繁GC避免换出

第四层:解决方案
  1. 临时措施

    sysctl vm.swappiness=0  # 禁用SWAP倾向
    swapoff -a              # 关闭SWAP分区(需确保物理内存≥SWAP用量)
    
  2. 永久配置

    vm.swappiness=0
    
  3. 效果验证

    • 关闭SWAP后Full GC耗时降至190ms(图2橙框)

基座:延伸思考
  1. 关键疑问解答

    • Q:SWAP是否必然导致GC卡顿?
      A:否!仅当GC时发生swap in操作才会引发(实名服务证明)
    • Q:JVM为何不禁用SWAP?
      A:Linux内存管理需平衡安全性与性能(kswapd守护进程机制)
  2. 最佳实践

    • 高并发服务建议:vm.swappiness=0 + 足够物理内存
    • 备选方案:降低堆大小(避免内存换出)
  3. 核心结论复述
    SWAP与GC同时触发→内存页换入换出→磁盘I/O阻塞STW→秒级卡顿,通过禁用SWAP或优化内存配置可根治。

原文参考https://blog.csdn.net/cnzzs/article/details/141273193


网站公告

今日签到

点亮在社区的每一天
去签到