核心是确认sendfile是否真走零拷贝路径并引发page cache积压:需strace验证sendfile64调用,监控/proc/meminfo中cached与pagetables变化,结合vm.vfs_cache_pressure和sendfile_max_chunk协同调优。

排查 Nginx 使用 sendfile 传输大文件时引发的内存波动,核心在于理解 sendfile 的内核行为、识别其与内存压力的耦合点,并结合系统级观测排除干扰因素。
确认 sendfile 是否真正启用并生效
Nginx 配置中开启 sendfile on 并不等于内核一定走零拷贝路径。需验证实际是否命中:
- 用
strace -e trace=sendfile64,splice -p $(pidof nginx)抓 worker 进程,下载大文件时观察是否调用sendfile64(注意:部分内核版本对超过 2GB 文件可能回退到用户态读写) - 检查
/proc/sys/net/core/wmem_max和 socket 发送缓冲区是否过小——sendfile在某些场景(如 TLS 加密、gzip 压缩或 proxy_buffering off 时的 upstream 转发)会绕过零拷贝,转而分配临时页缓存,导致 page cache 暴涨 - 确认文件系统是否支持
sendfile:ext4/xfs 通常支持,但 overlayfs、某些 NFS 客户端或加密文件系统(如 eCryptfs)可能强制回退到内核态复制
监控 page cache 与内存回收行为
sendfile 本身不分配用户态内存,但会大量占用 page cache(尤其是反复访问同一热文件时)。内存波动往往源于内核被迫频繁回收 page cache:
- 用
cat /proc/meminfo | grep -E "Cached|SReclaimable|PageTables"对比请求前后变化;若Cached暴增且PageTables同步上升,说明页表项膨胀(常见于大量小文件 or 高并发随机读) - 观察
/proc/vmstat中的pgpgin/pgpgout(换入换出量)、pgmajfault(主缺页次数):持续高值表明内存压力触发直接回收或 kswapd 活跃 - 使用
slabtop -o | grep -E "(dentry|inode|buffer_head)"查看 slab 分配器是否因元数据激增拖慢回收
调整 sendfile 相关内核与 Nginx 参数
避免 page cache 积压和回收抖动,需协同调优:
- 限制单次
sendfile传输长度:Nginx 中设置sendfile_max_chunk 512k(默认 0 表示无限制),防止一次系统调用锁定过大内存页区间 - 降低 page cache 滞留优先级:设
vm.vfs_cache_pressure=200(默认 100),加快 dentry/inode 回收;对纯静态文件服务可尝试vm.swappiness=1减少 swap 倾向 - 禁用不必要的缓冲:确保
tcp_nopush off(与sendfile冲突)、gzip off(压缩需读入用户态)、proxy_buffering off(反向代理时避免双重缓存)
区分真实故障与正常现象
并非所有内存波动都代表故障。以下属于预期行为,无需干预:
- 首次访问大文件时
Cached上升,后续访问稳定——这是 page cache 正常预热 - 内存波动幅度在可用内存 10% 以内,且无 OOM killer 日志(
dmesg | grep -i "killed process") -
free -h显示available值仍充足,即使used较高(Linux 将 page cache 视为可回收内存)
关键是把内存变化和具体请求模式、内核日志、page cache 行为对应起来,而不是孤立看 top 或 free 输出。定位到是 page cache 滞留、slab 膨胀还是回收机制卡顿,才能针对性优化。











