/dev/shm 被塞满会直接触发 OOM Killer 或导致进程写入失败,Redis、PostgreSQL、Java 等依赖共享内存的应用可能静默崩溃或拒绝服务;根本原因是 fork()、shm_open() 等系统调用在 /dev/shm 空间不足时失败,且默认 64MB 容量远低于实际需求,需通过限制大小、隔离路径和显式清理防范。

/dev/shm 被塞满会直接触发 OOM Killer 或导致进程写入失败,Redis、PostgreSQL、Java(尤其是使用 tmpfs 做堆外缓存或 JNI 共享内存的场景)都可能静默崩溃或拒绝服务——这不是配置问题,是资源耗尽的硬限制。
为什么 /dev/shm 满了会让 Redis 崩溃
Redis 默认使用 fork() 做 RDB 快照,而 Linux 的 fork() 在 /dev/shm 启用 tmpfs 且未设限的情况下,会尝试为写时复制(COW)页表分配共享内存空间。如果 /dev/shm 已满,fork() 返回 -1,Redis 日志里出现 Can't save in background: fork: Cannot allocate memory,接着主进程可能因 BGSAVE 失败重试、OOM 被杀,或降级为只读。
-
/dev/shm默认大小通常是 64MB(部分发行版为 2GB),远小于 Redis 实际内存占用,极易触顶 - 即使没开
save配置,某些模块(如 Redis Modules 使用malloc+mmap(MAP_SHARED))也会往/dev/shm写临时段 -
vm.overcommit_memory=2下更敏感:内核严格校验可用内存,fork()失败概率陡增
PostgreSQL 的 shared_buffers 不走 /dev/shm,但 wal_buffers 和 parallel query 可能踩坑
PostgreSQL 主内存不依赖 /dev/shm,但它在启用 huge_pages=on 且系统配置了 hugetlb_shm_group 时,会通过 shmget() 创建大页共享内存段;更常见的是 WAL buffer 刷盘前的临时映射、并行查询 worker 进程间通信(DynamicSharedMemory)——这些路径在高并发写入或并行度 > 10 时,会密集创建/销毁匿名 tmpfs 文件,堆积在 /dev/shm。
- 典型错误:
could not resize shared memory segment "/PostgreSQL.XXXXX": No space left on device -
pg_stat_activity中大量parallel worker状态为idle in transaction (aborted),实为 shm 分配失败后回滚 - 检查方法:
ls -l /dev/shm | grep PostgreSQL | wc -l,超 500 个临时段就值得警惕
Java 应用(尤其 Spring Boot + Netty / JNA)悄悄往 /dev/shm 写东西
多数人以为 Java 只用堆和本地内存,但 Netty 的 EpollEventLoop、JNA 的 NativeLibrary 加载、甚至某些国产 JDK(如 Dragonwell)的 G1ConcRefinementThreads 共享缓冲区,都会调用 shm_open() 创建命名信号量或环形缓冲区,默认落点就是 /dev/shm。这些文件不会自动清理,重启 JVM 也不删——除非显式调用 shm_unlink()。
立即学习“Java免费学习笔记(深入)”;
- 现象:
java.io.IOException: No space left on device,但df -h显示磁盘充足,df -h /dev/shm却是 100% - JVM 参数如
-XX:+UseG1GC本身不写/dev/shm,但搭配io.netty:netty-transport-native-epoll就会 - 排查命令:
find /dev/shm -name "*java*" -o -name "*netty*" -o -name "*jna*" -ls
怎么安全清理和长期防住
别直接 rm -rf /dev/shm/*——正在使用的 shm 对象被删会导致进程 SIGSEGV。必须先识别谁在用,再优雅释放。
- 查占用者:
lsof +D /dev/shm(需 root)或ipcs -m -p看 shmid 关联的 pid - 清空前先停对应服务:比如
systemctl stop redis再find /dev/shm -name "redis-*" -delete - 永久方案不是扩容,而是限制+隔离:
– 修改/etc/fstab行:tmpfs /dev/shm tmpfs defaults,size=1g,mode=1777 0 0(避免默认 64MB)
– 给关键服务分配独立 shm 目录:mkdir /dev/shm/redis && mount --bind /dev/shm/redis /dev/shm(仅限单实例)
– Java 应用加启动参数:-Dio.netty.native.workdir=/tmp/netty-shm,避开/dev/shm
最麻烦的不是清理,是那些没显式关闭 shm 的 C/C++ 扩展或老旧 JNI 库——它们可能只在进程退出时才释放,而 Java 进程又常驻不退。这种 case 必须翻源码确认 shm_unlink 调用点,否则监控再全也拦不住泄漏。










