MacOS 持久化配置,彻底解决 too many open files in system 的问题,包含 launchctl limit 配置和系统级限制调整方法
作为一个开发者,经常在 MacOS 遇到 Too many open files in system 的报错,尤其是碰到黑洞 node_modules 时,如何持久化配置彻底解决,直接上代码。
诊断步骤
先确认当前限制值,以及哪个进程消耗了大量文件描述符。
查看当前限制
# 系统级软硬限制
launchctl limit maxfiles
# 当前 Shell 的限制
ulimit -n
# 内核级限制
sysctl kern.maxfiles
sysctl kern.maxfilesperproc输出示例(已调整后):
$ launchctl limit maxfiles
maxfiles 10240 102400
$ sysctl kern.maxfiles
kern.maxfiles: 102400
$ sysctl kern.maxfilesperproc
kern.maxfilesperproc: 81920
这三个值是不同层面的限制,需要协调调整:
launchctl limit maxfiles(软/硬)— 每个进程的文件描述符限制,相当于 Linux 的nofilekern.maxfiles— 系统全局文件描述符上限kern.maxfilesperproc— 内核层面每进程文件描述符上限
找出文件描述符消耗大户
# 列出所有进程的文件描述符数量(前 10)
lsof -n 2>/dev/null | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# 查看特定进程的打开文件数
lsof -p <PID> | wc -l
# 查看特定进程的限制和使用量
launchctl limit pid <PID>
# 或
cat /proc/<PID>/limits # Linux; macOS 无此文件常见消耗大户:Node.js(node_modules 大量文件)、Java 应用、IDE(WebStorm、VS Code)、Docker。
持久化配置
创建文件 sudo vi /Library/LaunchDaemons/limit.maxfiles.plist,内容如下,可根据自己爱好改后面的两个数字值。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>10240</string>
<string>102400</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>验证文件格式和内容,并应用生效。
plutil /Library/LaunchDaemons/limit.maxfiles.plist
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
再次输入 launchctl limit maxfiles 查看是否生效。
注意:plist 中的两个数字分别是「软限制」和「硬限制」。软限制是实际生效的限制,硬限制是软限制能够调高的上限。进程可自行将软限制调至硬限制。(实际上大多数用户都直接以 root 启动,这两者的区别不大。)
kernel 层面的调整
部分场景下仅调整 launchctl limit 不够,还需调整内核参数。macOS 没有 /etc/sysctl.conf,需要通过启动参数或 sysctl 临时设置:
# 临时生效(重启后失效)
sudo sysctl -w kern.maxfiles=102400
sudo sysctl -w kern.maxfilesperproc=81920持久化 kernel 参数需要创建 /Library/LaunchDaemons/sysctl.maxfiles.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>sysctl.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/sysctl</string>
<string>-w</string>
<string>kern.maxfiles=102400</string>
<string>kern.maxfilesperproc=81920</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>sudo launchctl load -w /Library/LaunchDaemons/sysctl.maxfiles.plist各 Shell 级别的 ulimit 配置
Shell 初始化时也可能覆盖限制。在 ~/.zshrc 或 ~/.bashrc 中添加:
ulimit -n 10240针对特定工具的额外配置
- Docker Desktop: Preferences → Resources → Advanced → 调整资源限制
- VS Code: 在
settings.json中"files.watcherExclude"排除**/node_modules/**,减少文件监控数 - WebStorm: Preferences → Appearance & Behavior → System Settings → 勾选 “Use ‘safe write’”
问题排查清单
launchctl limit maxfiles确认进程级软/硬限制sysctl kern.maxfiles kern.maxfilesperproc确认内核级限制ulimit -n确认当前 Shell 限制lsof -p <PID> | wc -l定位具体进程的文件描述符使用量- 如果是 GUI 应用,注意
launchctl limit对 GUI 应用可能不生效——GUI 应用由launchd用户域管理,需在~/Library/LaunchAgents/下单独配置