磁盘又红了。
macOS 系统设置里那个存储条变成刺眼的红色——994.7 GB 的 APFS 容器,已经用了 822 GB,只剩 65 GB 可用,93% 满。这台 M4 MacBook Pro 才用了不到一年,怎么就塞满了?
我决定做一次彻底的只读审计,搞清楚空间到底去了哪里,然后再决定删什么。
审计方法
扫描范围
整个用户主目录,不排除任何子目录。扫描内容包括:
- 大文件(≥100 MB)
node_modules目录.git仓库- iCloud / 云同步本地副本
- 系统和 App 缓存
- Applications 及其关联数据
- iOS Simulator 运行时(独立 APFS 容器)
- 开发工具缓存(Homebrew、npm、pip、uv、Maven 等)
扫描用的是 find + stat + du,全部只读,不删任何东西。最后把结果写进 CSV 和 report.md。
工具
为了方便逐项决策,还搞了一个单文件 HTML 交互页面——每条清理候选项都有 checkbox、风险 badge(HIGH/MEDIUM/LOW)、物理大小、推荐清理命令。勾选后底部会自动生成带二次确认的 shell 脚本。配了一个 Python 小服务器(stdlib 写的,50 行),可以把决策和脚本保存到本地 saves/ 目录。
最大的发现:291 GB 的幽灵文件
审计第一版用 stat -f %z 统计大文件,汇总出 647 GB——我心想这也太多了。
后来对比另一份审计(用 du -sk / stat -f %b 统计物理占用),发现数字对不上:
| 维度 | 数值 |
|---|---|
| 大文件逻辑总和 | 647.36 GB |
| 大文件物理总和 | 356.04 GB |
| 差额(占位符 + 稀疏文件) | 291.32 GB |
220 个文件的物理占用是 0 字节。
这些文件在 Finder 里看起来完全正常——有图标、有大小、双击能打开(会触发下载)。但它们已经被 macOS 的 “Optimize Mac Storage” 功能 evict 到了 iCloud,本地只留了一个 0 字节的占位符。stat -f %z(逻辑大小)照样报 22 GB,但 stat -f %b(512 字节块数)返回 0。
举几个典型的:
stat -f "logical=%z blocks=%b" ~/Documents/movies/fcp_iww2proj4.ziplogical=24502598175 blocks=0一个 22.8 GB 的 Final Cut Pro 项目包——物理占用 0。它在 Documents 文件夹里(不是 iCloud Drive),但因为 macOS 开启了 Desktop & Documents Sync,这个文件已经被 evict 到云端了。
同样的情况发生在 ~/Library/Mobile Documents/com~apple~CloudDocs/Archives/LargeDownloads/ 下面所有 Blender 大场景文件(80+ GB 逻辑)、ISO 镜像、屏幕录制——全部是 0 字节占位符。
教训在 APFS + iCloud 环境下,所有清理决策必须基于物理大小。逻辑大小只能用于发现文件数量和分布,不能用于估算可释放空间。
du -sk给的是物理大小,stat -f %z给的是逻辑大小——两者在 iCloud 管理的文件上可以差出整个数量级。
另外一个有意思的例子是 Claude 桌面客户端的 VM bundle:
logical=10737418240 blocks=16153376# 10 GB 逻辑 → 7.7 GB 物理这是 APFS 稀疏文件——文件内部有大量未写入的区域,APFS 不为它们分配物理块。
真正占空间的是什么
排除占位符后,真正占盘的大头:
| 类别 | 物理占用 |
|---|---|
| Parallels Windows VM | 76.4 GB |
Steam 游戏录像 ~/Movies/steamrecording | 70.4 GB |
| iPhone/iPad 本地备份 | 74.4 GB |
| LM Studio 本地模型 | 32.1 GB |
~/Library/Caches(各种缓存) | 26.0 GB |
| WeChat 聊天数据 | 24.8 GB |
~/Codes/novel_dateset/novels.db | 24.0 GB |
| uv cache(Python 包缓存) | 17.8 GB |
| Movies/Dowine(下载的视频) | 17.8 GB |
| iOS/watchOS Simulator Runtimes | 107.5 GB |
| node_modules(110 个目录) | 9.6 GB |
| npm cache | 8.3 GB |
iOS Simulator Runtimes 是个容易被忽略的大头——它们不在 ~/Library 里,而是作为独立的 APFS 容器挂载在 /Library/Developer/CoreSimulator/Volumes/。df -h 能看到 disk5 到 disk19 这些卷。我装了 iOS 18.1、18.2、26.2、26.4 四个版本加上两个 watchOS,合计 107 GB。
意外收获:OpenWrt 路由器也满了
清理 Mac 之余,本来想把一些重要文件 rsync 到路由器的外挂硬盘上做个冷备份。SSH 进去一看,备份还没开始就 abort 了——这台跑 OpenWrt 的飞凡 N1 盒子(也是 TimeMachine 的备份目标),Docker 数据分区已经满了:
df -h /mnt/mmcblk2p4Filesystem Size Used Available Use% Mounted on/dev/mmcblk2p4 4.5G 4.5G 0 100% /mnt/mmcblk2p4Docker 数据分区 100% 满。
查了一下原因:
du -sk /mnt/mmcblk2p4/docker/containers/* | sort -n | tail -3最大的容器日志:
| 大小 | 容器 |
|---|---|
| 422.8 MB | node_exporter(Prometheus 指标采集器) |
| 212.5 MB | prometheus |
node_exporter 每 15 秒报一条相同的错误:
collector failed name=arp err="could not get ARP entries:rtnetlink NeighMessage has a wrong attribute data length"OpenWrt 5.15 内核的 netlink 消息格式和 node_exporter v1.7.0 预期的对不上,ARP collector 每次采集都失败,每次失败都写一行日志。跑了 14 个月没人管,日志涨到 422 MB。加上 Prometheus 自己的日志 212 MB,两个加起来把 4.5 GB 的分区塞满了。
修法很简单——截断日志 + prune dangling 镜像,从 100% 降到 82%。然后改 /etc/docker/daemon.json 加 log-opts 的 max-size 和 max-file,重启 dockerd 让 rotation 生效。
清理过程
回到 Mac。逐类过了一遍:
Apps(释放 ~52 GB)
用交互网页逐个决定。大头是:
- LM Studio + 全部模型:1.36 GB App + 32.1 GB 模型权重,全删(以后需要了重新下)
- Apple 创作套件(iMovie / Motion / Logic Pro / MainStage / GarageBand):10.24 GB,全删
- MultiMC:7.98 GB,先备份存档到 iCloud 再删
- Final Cut Pro:5.57 GB,早期从 Apple 零售店机器上拷过来的,现在 Apple 加了正版验证打不开了,删
- 零散 App 二十几个:Blender、Codex.app、ChatGPT Atlas、Antigravity、Firefox、ComfyUI 等
一个坑:Apple 系统 App(Final Cut / iMovie / GarageBand 等)有 SIP 保护,rm -r 不会报错但也不会删除——静默失败,文件还在原地。必须用 sudo rm -r 或在 Finder 里拖进废纸篓(会弹密码框)。
另一个容易忽略的:删 App 不等于删数据。QQ.app 本体 884 MB,但 ~/Library/Containers/com.tencent.qq 还有 8 GB 聊天数据。Whisky(Wine 容器)App 本体已经不在了,但 Containers 里还留着 2.26 GB 的 Wine bottles。审计时把所有已删 App 的残留 Application Support / Containers / Caches 都扫了一遍,额外清出 3.6 GB。
缓存(释放 ~25 GB)
| 项目 | 释放 | 命令 |
|---|---|---|
| uv cache | 12.3 GB | uv cache prune |
| pip cache | 4.4 GB | python -m pip cache purge |
| npm cache | 8.3 GB | npm cache clean --force |
| Xcode DerivedData | 4.3 GB | 直接 rm -r |
| Homebrew 旧版本 | 836 MB | brew cleanup |
| ms-playwright | 1.9 GB | 直接删 |
| vscode-cpptools | 1.5 GB | 直接删 |
| HuggingFace cache | 1.9 GB | 直接删 |
uv cache 的 prune 比 clean 好——只清未被当前项目引用的缓存,保留活跃依赖。清完后从 17.8 GB 降到 4.7 GB,说明有 12 GB 是过期的旧版本包。
pip cache 4.4 GB 全是历史遗留。机器上 pip 已经被禁用(pip is disabled. Use: uv pip cache info),这些缓存完全没用。
iOS Simulator Runtimes(释放 ~19 GB)
xcrun simctl runtime delete <uuid>删掉了 iOS 18.1、iOS 26.2、watchOS 26.2 三个不再需要的版本。保留 iOS 18.2(有 iOS 18 设备)和 iOS 26.4(当前开发用)。
大文件(释放 ~33 GB)
- Claude VM bundles(11.2 GB):Claude 桌面客户端的虚拟机,删了下次打开会重建
- Movies/Dowine(17.8 GB):下载的课程录屏和游戏视频,全删
- 流浪地球2-hvc1.mp4 在 Movies/ 和 Movies/TV/ 各一份(重复),删一份 3.3 GB
- 重复 zip:B9/A9/C晓说小说合集在 Downloads 和 Codes/Datasets 各一份,删 Downloads 那份 16.3 GB
Downloads 文件夹
最后处理 Downloads——39.8 GB / 849 个文件。MD5 去重发现 165 个完全相同的重复文件(各种 (1).pdf (2).pdf)。
课程材料打包成 zip 备份到 iCloud/Archives,工作文档移到 iCloud/Documents/Work-BOC(不压缩,保持 Spotlight 可搜),个人文件(签证、租约、收据)移到 iCloud/Documents/Personal,剩余全部移到 iCloud/Downloads。
APFS 快照的坑
清理过程中有一个让人困惑的现象:明明删了 50+ GB 的文件,df 显示的可用空间不升反降。
原因是 Time Machine 本地快照。macOS 会定期创建 APFS 本地快照作为 Time Machine 的快照源。快照引用了被删文件的旧版本,空间无法释放——直到快照过期或被手动删除。
tmutil listlocalsnapshots /com.apple.TimeMachine.2026-05-24-121559.localcom.apple.TimeMachine.2026-05-25-153221.localcom.apple.os.update-... # 3 个 OS update 快照删掉两个 TM 快照后:
tmutil deletelocalsnapshots 2026-05-24-121559tmutil deletelocalsnapshots 2026-05-25-153221空间瞬间释放——Data 卷从 862 GB 降到 720 GB,可用空间从 26 GB 跳到 199 GB。之前所有删除操作累积的空间一次性回来了。
TIP如果你在 Mac 上删了很多文件但
df不见变化,先查tmutil listlocalsnapshots /。删掉不需要的本地快照就行。OS update 开头的快照不要动——那些是系统保护用的。
最终结果
| 之前 | 之后 | |
|---|---|---|
| Data 卷已用 | 822 GB | 679 GB |
| 可用 | 65 GB | 209 GB |
| 占比 | 93% | 77% |
| 释放 | — | 144 GB |
| System Report 可用(含 purgeable) | ~65 GB | ~457 GB |
System Report 报的 457 GB 比 df 的 209 GB 大很多,是因为它把 purgeable space(iCloud 占位符 + 系统可自动回收的缓存)也算进了”可用”。这 ~248 GB 的差额就是之前发现的那些 iCloud evicted 文件——系统知道在磁盘紧张时可以不重新下载它们,所以算作可调配空间。
顺手做的小事
清理过程中还顺手做了几件事:
经验总结
-
stat -f %z≠ 实际占用。APFS + iCloud 环境下,逻辑大小和物理大小可以差出数百 GB。审计磁盘一定要用du -sk(物理)或stat -f %b(块数 × 512),不能用stat -f %z。 -
iOS Simulator Runtimes 是隐形杀手。它们作为独立 APFS 容器存在,不在
~/Library里,du -sh ~扫不到。用xcrun simctl runtime list查看,xcrun simctl runtime delete清理。 -
删文件后空间不释放 → 检查 APFS 快照。
tmutil deletelocalsnapshots是解药。 -
Docker 日志不配 rotation 迟早爆盘。
daemon.json里加"log-opts": {"max-size": "10m", "max-file": "7"},然后必须重启 dockerd——已存在的容器不会自动应用新的 daemon defaults。 -
删 App 要连带清数据。
~/Library/Application Support/、~/Library/Containers/、~/Library/Group Containers/、~/Library/Caches/里经常有比 App 本体大好几倍的残留数据。