磁盘I/O是Java线上系统慢、超时及线程卡在java.io调用的隐藏元凶,需用sar -d查历史趋势(如sar -d 5 720),关注await、svctm、%util;再用iostat -x 1看实时设备级指标,结合await、avgqu-sz和吞吐量判断瓶颈层级。

Java 线上系统出现慢、超时或线程卡在 java.io 调用时,磁盘 I/O 往往是隐藏元凶。单靠 top 看不到真实瓶颈,必须用 sar 和 iostat 配合定位——前者看“历史趋势+全局压力”,后者看“实时设备级细节+进程源头”。
先用 sar -d 查历史磁盘压力趋势
Java 应用的 I/O 问题常有周期性(如定时日志刷盘、定时任务批量写库),sar 能回溯问题发生时刻:
- 查最近 1 小时每 5 秒的磁盘活动:
sar -d 5 720(720 × 5s = 1h) - 重点看
await(平均等待毫秒)、svctm(服务时间)、%util(设备利用率)三列 - 若
await持续 >30ms(SSD)或 >50ms(NVMe),且%util同步飙升,说明磁盘已排队;若await高但%util仅 40%~60%,问题大概率在内核或文件系统层(比如 ext4 journal 延迟、fsync 频繁) - 对比业务异常时间点,确认 I/O 峰值是否与 Java 日志滚动、数据库批量导入等操作重合
再用 iostat -x 1 实时抓取设备级指标
iostat -x 1 每秒刷新,聚焦三个联动指标判断是否真瓶颈:
- await >30ms + avgqu-sz >4(SSD)或 >8(NVMe)+ rkB/s + wkB/s 远低于理论带宽 → 不是磁盘慢,是上层下发过载(如 Java 应用高频小 write() + flush())
- await 正常( → 设备正在高效处理大块顺序 I/O(例如 JVM GC 日志归档),属健康状态,无需干预
-
高 await + 低 %util → 请求卡在内核路径,常见于:Logback 配置了
immediateFlush=true、MySQL 的sync_binlog=1、或应用未复用 FileWriter 导致反复 open/write/close 小文件
配合 iotop 和 pidstat 锁定 Java 进程行为
iostat 告诉你“哪块盘忙”,iotop 和 pidstat 才能告诉你“哪个 Java 进程在打盘”:
立即学习“Java免费学习笔记(深入)”;
- 运行
sudo iotop -o -P(只显示有 I/O 的进程,按速率排序),找java进程的IO_RATE是否远高于其他进程 - 对可疑 PID 执行
pidstat -d -p [PID] 1 5,观察其r/s、w/s和avgqu-sz是否同步异常升高 - 用
lsof -p [PID] | grep -E '\.log|\.data|/var/log'查它正写的文件路径,再检查对应目录是否在慢盘(如机械盘挂载的 /data)、是否有日志轮转脚本误删导致频繁重建 inode
别跳过基础配置检查
很多“I/O 瓶颈”其实是 Java 或系统配置不当引发的假象:
- 检查挂载选项:
mount | grep $(df . | tail -1 | awk '{print $1}'),确认是否用了noatime,barrier=0,data=writeback等优化项(生产环境慎用barrier=0) - 查看 IO 调度器:
cat /sys/block/sda/queue/scheduler,NVMe 推荐none,SSD 用mq-deadline,机械盘保留cfq(旧内核)或bfq - 确认 JVM 参数:
-XX:+UseG1GC下若G1MixedGCCountTarget过低,会导致频繁混合回收并触发大量日志写入;-Xloggc路径若落在高延迟盘上,也会拖累整体响应


















