io_wait高表明系统存在磁盘或网络io瓶颈,需通过iostat、iotop等定位设备与进程,结合lsof、jstack、jstat分析java应用的文件操作、日志配置及gc关联影响,并交叉验证排除干扰。

IO_Wait高通常说明CPU在等待磁盘或网络IO完成,而Java应用本身并未真正“忙”,但系统层面已出现瓶颈。排查重点不是Java线程是否在IO上阻塞(JVM线程状态可能仍是RUNNABLE),而是定位底层IO压力来源——可能是磁盘慢、日志刷盘频繁、文件句柄泄漏、NFS挂载异常、或GC导致的临时IO放大等。
看系统级IO负载和瓶颈设备
先确认是不是真有IO压力,以及压在哪类设备上:
- iostat -x 1:重点关注 %util(设备饱和度)、await(平均IO响应时间)、r_await/w_await。若某块盘 %util 接近100% 且 await > 20ms,基本可判定是它拖慢了系统;
- iotop -o -P:只显示实际在做IO的进程,按IO_RATE排序,快速锁定是 Java 进程本身(如 java 或 jsvc)还是其他进程(如 rsync、logrotate、backup 脚本)在刷盘;
- cat /proc/diskstats:对比读写扇区数增长速率,辅助验证 iostat 结果;
- 检查是否用了 NFS/Ceph/云盘等远程存储:这些延迟天然更高,await 易飙升,且错误重试会进一步加剧 wait 时间。
查Java进程的IO行为特征
即使 iotop 显示 Java 进程IO量大,也要区分是合理行为(如批量导出)还是异常行为(如日志狂打、临时文件失控):
- 用 lsof -p
| grep -E "(REG|DIR)" | wc -l 看打开文件数是否异常增长(比如从几千涨到几万),再结合 lsof -p| awk '$5~/REG/ {print $9}' | xargs ls -lh 2>/dev/null | sort -k5,5hr | head -20 找出最大的几个文件(常是日志、dump、缓存文件); - 检查日志配置:logback.xml 或 log4j2.xml 中是否启用了同步刷盘(immediateFlush=true)、滚动策略不合理(如 TimeBasedRollingPolicy 未配 maxHistory)、或日志级别为 DEBUG 且打印高频业务字段;
- 用 jstack
搜索 java.io.RandomAccessFile、FileOutputStream.write、Channel.write 等堆栈,看是否有线程长期卡在 write() 调用上(注意:此时线程状态仍是 RUNNABLE,不是 BLOCKED 或 WAITING); - 检查是否用了 MappedByteBuffer 或大量 FileChannel.map():这类操作会触发内核页缓存回写,尤其在内存紧张+脏页多时,会引发突发性 IO_Wait。
关联GC与IO的隐性耦合
GC 本身不直接产生IO,但在某些场景下会显著放大IO压力:
- 频繁 CMS 或 G1 的 Mixed GC 可能触发大量对象晋升到老年代,若老年代使用 ConcMarkSweepGC 且开启 -XX:+UseParNewGC,并发阶段虽不STW,但会增加内存拷贝和元数据更新,间接加剧 page cache 压力;
- 更关键的是:GC 后内存释放 → JVM 缓存(如堆外缓存、Netty PooledByteBufAllocator)可能主动刷盘或重建 → 触发批量IO;
- 用 jstat -gc
1000 观察 GC 频率与 IO_Wait 高峰是否时间吻合;同时用 vmstat 1 看 si/so(swap in/out)是否同步升高——若 so 高,说明物理内存不足,内核频繁换页,IO_Wait 自然飙升。
验证与收口:排除干扰、缩小范围
避免被表象误导,需交叉验证:
- 停掉应用日志(如重定向 stdout/stderr 到 /dev/null,或临时注释 log appender),观察 IO_Wait 是否下降 —— 若明显下降,问题就在日志;
- 用 strace -p
-e trace=write,open,close,fsync -s 128 2>&1 | grep -E "(log|tmp|cache)" 实时抓取Java进程的文件系统调用,确认写入路径和频率; - 检查磁盘健康:smartctl -a /dev/sdX(物理机)或云平台提供的磁盘监控(如 AWS CloudWatch 中 VolumeIdleTime 持续偏低、BurstBalance 耗尽);
- 确认没有定时任务干扰:如 crontab 每分钟执行 find /var/log -name "*.log" -mmin +60 -delete,这种扫描+删除也会拉高 IO_Wait。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











