2437 字
12 分钟
从 93% 到 77%:一次 Mac 磁盘大扫除的完整记录

磁盘又红了。

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。

举几个典型的:

Terminal window
stat -f "logical=%z blocks=%b" ~/Documents/movies/fcp_iww2proj4.zip
logical=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 VM76.4 GB
Steam 游戏录像 ~/Movies/steamrecording70.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.db24.0 GB
uv cache(Python 包缓存)17.8 GB
Movies/Dowine(下载的视频)17.8 GB
iOS/watchOS Simulator Runtimes107.5 GB
node_modules(110 个目录)9.6 GB
npm cache8.3 GB

iOS Simulator Runtimes 是个容易被忽略的大头——它们不在 ~/Library 里,而是作为独立的 APFS 容器挂载在 /Library/Developer/CoreSimulator/Volumes/df -h 能看到 disk5disk19 这些卷。我装了 iOS 18.1、18.2、26.2、26.4 四个版本加上两个 watchOS,合计 107 GB。

意外收获:OpenWrt 路由器也满了#

清理 Mac 之余,本来想把一些重要文件 rsync 到路由器的外挂硬盘上做个冷备份。SSH 进去一看,备份还没开始就 abort 了——这台跑 OpenWrt 的飞凡 N1 盒子(也是 TimeMachine 的备份目标),Docker 数据分区已经满了:

Terminal window
df -h /mnt/mmcblk2p4
Filesystem Size Used Available Use% Mounted on
/dev/mmcblk2p4 4.5G 4.5G 0 100% /mnt/mmcblk2p4

Docker 数据分区 100% 满

查了一下原因:

Terminal window
du -sk /mnt/mmcblk2p4/docker/containers/* | sort -n | tail -3

最大的容器日志:

大小容器
422.8 MBnode_exporter(Prometheus 指标采集器)
212.5 MBprometheus

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.jsonlog-optsmax-sizemax-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 cache12.3 GBuv cache prune
pip cache4.4 GBpython -m pip cache purge
npm cache8.3 GBnpm cache clean --force
Xcode DerivedData4.3 GB直接 rm -r
Homebrew 旧版本836 MBbrew cleanup
ms-playwright1.9 GB直接删
vscode-cpptools1.5 GB直接删
HuggingFace cache1.9 GB直接删

uv cache 的 pruneclean 好——只清未被当前项目引用的缓存,保留活跃依赖。清完后从 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)#

Terminal window
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 的快照源。快照引用了被删文件的旧版本,空间无法释放——直到快照过期或被手动删除。

Terminal window
tmutil listlocalsnapshots /
com.apple.TimeMachine.2026-05-24-121559.local
com.apple.TimeMachine.2026-05-25-153221.local
com.apple.os.update-... # 3 个 OS update 快照

删掉两个 TM 快照后:

Terminal window
tmutil deletelocalsnapshots 2026-05-24-121559
tmutil deletelocalsnapshots 2026-05-25-153221

空间瞬间释放——Data 卷从 862 GB 降到 720 GB,可用空间从 26 GB 跳到 199 GB。之前所有删除操作累积的空间一次性回来了。

TIP

如果你在 Mac 上删了很多文件但 df 不见变化,先查 tmutil listlocalsnapshots /。删掉不需要的本地快照就行。OS update 开头的快照不要动——那些是系统保护用的。

最终结果#

之前之后
Data 卷已用822 GB679 GB
可用65 GB209 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 文件——系统知道在磁盘紧张时可以不重新下载它们,所以算作可调配空间。

顺手做的小事#

清理过程中还顺手做了几件事:

  • blackAppleRegression 项目推到 GitHub(public)。Bad Apple!! 用递归模式匹配渲染的那个。视频从 mpeg4 转码成 H.264(QuickTime 播不了 mpeg4),上传了 YouTubeBilibili
  • dashboard 项目推到 GitHub(private)。飞凡 N1 盒子上跑的那个 kiosk 仪表盘
  • 全局 CLAUDE.md 里加了 “prefer pnpm over npm” 的规则——pnpm 的 hardlink content-addressable store 能跨项目去重 node_modules

经验总结#

  1. stat -f %z ≠ 实际占用。APFS + iCloud 环境下,逻辑大小和物理大小可以差出数百 GB。审计磁盘一定要用 du -sk(物理)或 stat -f %b(块数 × 512),不能用 stat -f %z

  2. iOS Simulator Runtimes 是隐形杀手。它们作为独立 APFS 容器存在,不在 ~/Library 里,du -sh ~ 扫不到。用 xcrun simctl runtime list 查看,xcrun simctl runtime delete 清理。

  3. 删文件后空间不释放 → 检查 APFS 快照tmutil deletelocalsnapshots 是解药。

  4. Docker 日志不配 rotation 迟早爆盘daemon.json 里加 "log-opts": {"max-size": "10m", "max-file": "7"},然后必须重启 dockerd——已存在的容器不会自动应用新的 daemon defaults。

  5. 删 App 要连带清数据~/Library/Application Support/~/Library/Containers/~/Library/Group Containers/~/Library/Caches/ 里经常有比 App 本体大好几倍的残留数据。

从 93% 到 77%:一次 Mac 磁盘大扫除的完整记录
https://blog.lishuyu.app/posts/mac-disk-cleanup-audit-2026/
作者
猫猫魔女
发布于
2026-05-25
许可协议
CC BY-NC-SA 4.0