先说结论,省得你翻到最后:我手上这台 M1 MacBook Air(8GB 内存 / 256GB 硬盘,2021 年 3 月官网买的,现在跑 macOS 15.5),这两周最夸张的一次 sysctl vm.swapusage 显示 used = 2143.75M,而活动监视器里内存压力还是绿的。所以「交换空间一有数字就说明内存不够」这个说法,至少在这台机器上不成立。我甚至在它 swap 到 2GB 的时候剪完了一条 4 分钟的 1080p 视频,预览没掉帧。
但这不代表 8GB 万事大吉。同一台机器在另一件事上翻过车——同时开 Docker Desktop 和 Xcode 编译,切回微信要等一秒多,白屏。那一秒不是硬盘的锅,也不是 swap 的锅,后面细说。
第一步:把「交换使用量」这个指标从脑子里删掉
macOS 从 Mavericks(10.9)开始引入内存压缩。逻辑是:物理内存不够时,系统先试着把不活跃的内存页压缩起来,压缩后还不够,才写到磁盘上的 swap 文件。而且 Apple Silicon 上的 swap 是加密的,你跑 sysctl vm.swapusage 会看到末尾挂着一个 (encrypted)。
所以「已使用的交换空间」这个数字本身没有意义,它是结果不是原因。真正该看的是内存压力,它综合了空闲内存、压缩器负担、换页频率,最后给你一个绿黄红的判断。
活动监视器怎么开:Command + 空格 输入「活动监视器」,或者终端敲 open -a 'Activity Monitor'。点顶部「内存」标签,左下角那条曲线就是内存压力,右边有物理内存、已使用的内存、已缓存的文件、已使用的交换空间四个格子。重点看曲线形状,不是看颜色。一条平的绿线和一个锯齿状但整体还是绿的线,是两种完全不同的状态。锯齿意味着系统在反复压缩-解压,那才是你能感觉到卡的地方。
用 vm_stat 把「感觉」变成数字
终端敲 vm_stat,不需要 sudo。Apple Silicon 的 page size 是 16384 字节,Intel Mac 是 4096 字节,这个区别挺关键,因为所有数字都要乘 page size 才是字节数。
我抄一段某天下午的输出(手抄进备忘录的,格式可能有误差):
Mach Virtual Memory Statistics: (page size of 16384 bytes)
Pages free: 273456.
Pages active: 321456.
Pages inactive: 298765.
Pages speculative: 12345.
Pages wired down: 98123.
Pages purgeable: 4321.
Translation faults: 98765432.
Pages copy-on-write: 1234567.
Exchanges of pages to a file: 98765.
Pages zero filled: 45678901.
Pages reactivated: 234567.
Pages purged: 123456.
File-backed pages: 301234.
Anonymous pages: 432109.
Pages stored in compressor: 65432.
Pages occupied by compressor: 23456.
Decompressions: 123456.
Compressions: 234567.
Pageins: 345678.
Pageouts: 1245678.
Swapins: 12345.
Swapouts: 23456.
这里面我其实只看两行。Pages occupied by compressor 是压缩器实际占了多少物理内存,23456 乘 16384 约等于 384MB。Pages stored in compressor 是被压进去的原始数据有多少页,65432 乘 16384 约等于 1.02GB。两者一除,1.02GB 塞进 384MB,压缩比大约 2.7 : 1。
这个比值比绝对值有意思得多。2.7 倍意味着系统用 384MB 换回了 640MB 的有效内存。我观察它在 2.0 到 3.0 之间波动,取决于里面装的是什么——文本和代码压缩率高,已经压过的图片视频压缩率低。这也解释了为什么有人压缩器占 1.5GB 还很流畅,有人占 500MB 就卡,装的类型不一样。
顺带一提,Swapins 和 Swapouts 这两个计数器跟 sysctl vm.swapusage 里的 used 对不上数,我算了三次都对不上,估计统计口径不同,但没找到官方文档写清楚这件事。
sysctl vm.swapusage 和 memory_pressure
跑 sysctl vm.swapusage,我此刻的输出是:
vm.swapusage: total = 2048.00M used = 0.00M free = 2048.00M (encrypted)
注意 total 会变。系统一开始给 2GB 的 swap 文件,不够了往上加,我见过 total = 4096.00M 的时候。这说明苹果的策略是按需扩容,不是预分配一大块。
再跑 memory_pressure,它会采一遍样,最后打印一行 System-wide memory free percentage: XX%。我这台平常在 40% 到 55% 之间,跑 Xcode 编译会掉到 20% 出头。低于 10% 基本就是开始杀后台进程了,你的微信或邮件会莫名其妙自己重启,那就是 jetsam 干的。
我这两周抄下来的四个场景
| 场景 | 内存压力 | 压缩器占用 | swap used | 切后台 App 延迟 |
|---|---|---|---|---|
| Safari 12 标签 + 微信 + 飞书 | 绿 | 310 MB | 0 | 测不出来 |
| 上面再加 Chrome 20 标签 | 绿 | 890 MB | 210 MB | 测不出来 |
| 上面再加 Xcode 编译 | 黄 | 1.6 GB | 1.2 GB | 偶尔 0.4 到 0.6 秒 |
| 上面再加 Docker Desktop(限 4GB) | 黄偏红 | 1.7 GB | 2.1 GB | 1 到 2 秒 |
「测不出来」的意思是我拿秒表按了十几次,落在 0.1 到 0.2 秒,跟空载没区别。注意第三行,到 1.2GB swap 都还行;第四行只多了 0.9GB,体验却断崖式下跌。差在哪?差在 Docker Desktop 跑的是一个 Linux 虚拟机,它的内存页活跃度极高,不像 Chrome 后台标签那样躺着不动。系统要不停地把它的页换出去再换回来,卡的是换页频率,不是总量。
这是我想说的第一个反常识的点:swap 总量跟卡不卡,相关性比你想的弱;换页活跃度才是关键。
第二个点关于 WindowServer。我接上 4K 60Hz 外接屏之后,WindowServer 从 280MB 涨到了 1.2GB。这跟内存够不够没关系,是它要持有外接分辨率的 framebuffer。所以如果你接 4K 或 5K 屏,8GB 机器上直接少 1GB 可用内存,这笔开销躲不掉。想省点可以把刷新率从 60Hz 降到 30Hz,或者用 2560×1440 缩放,但我不想这么干。
那个被说烂的「swap 伤 SSD」
网上最常见的说法是:swap 疯狂写入,会把焊死的 SSD 写坏。我算过一笔账。刚才 vm_stat 里 Pageouts 是 1245678 页,乘 16384 字节,两周大约 20GB 的页换出。但这 20GB 包含文件页,真正的 swap 写入要少得多。我按 vm.swapusage 的 used 变化粗估,一天大概 8 到 12GB 的量级。
一年 3.6TB,五年 18TB。而 256GB 的 NVMe 固态,TBW(总写入寿命)一般在 100 到 150TB 这个量级,不同主控差异很大,苹果也从没公开过具体数字。这么算,swap 写到 SSD 寿命到期的概率,比你把电脑用到淘汰还低。
真正会加速 SSD 老化的,我觉得是两个东西。一是长期高温,M1 Air 无风扇,满载跑半小时机身烫手,NAND 对温度是敏感的。二是磁盘长期写到 95% 以上,主控没有足够空闲块做磨损均衡和垃圾回收,写入放大系数会飙上去。所以「256GB 只剩 3GB 可用」比「swap 用了 2GB」,对硬盘的威胁大得多。
内存压力真红了,按这个顺序处理
别急着 sudo purge。那个命令清的是文件缓存(file-backed 的 inactive pages),对 App 自己占着的匿名内存一点用没有。我实测跑完 purge,Pages free 会从 27 万页跳到 60 万页,看着很爽,但五分钟后又回来了,因为系统会把内存重新拿去做缓存——这是它的设计,不是 bug。
我自己的排查顺序是这样的:
- 活动监视器里点「内存」列排序。先看第一名是不是 WindowServer 或者 kernel_task。是 WindowServer,多半是外接屏或者某个 App 在疯狂重绘;是 kernel_task,在 Intel Mac 上通常意味着机器在降频散热,跟你开多少 App 没关系。
- 找有没有单个进程 Real Memory 超过 2GB。Chrome 建议用菜单里的「更多工具 → 任务管理器」,它比活动监视器准,因为 Chrome 每个标签是独立进程,活动监视器会合并显示。Safari 好一些,但标签多了也一样。
- Docker Desktop 如果不是正在用,直接退出,别只是关窗口。它挂在菜单栏的时候,虚拟机还在后台跑。
- 实在不行再跑 purge 当个安慰剂(我这版系统不加 sudo 也能跑,看你系统版本)。
- 最后手段是重启。macOS 不像 Windows 那样有明显的泄漏积累,但第三方输入法、截图工具、常驻小工具是重灾区,我用的某截图工具开一周能吃 800MB。
到底该不该上 16GB
我的判断标准很粗暴:你这台机器有没有一个「加了内存就省事」的固定工作负载。浏览器 + 微信 + 飞书 + WPS + 看视频,8GB 在 M1、M2 上完全够,内存压力长期是绿的。网上那些「8GB 必卡」的视频,背景是 4K 时间线加一堆插件,不是普通人的日常。
但只要涉及下面任意一项,加钱上 16GB 是值得的:同时跑 Docker 或任何虚拟机;Xcode、Android Studio 频繁编译;Final Cut 或 DaVinci 的时间线超过 2 分钟的 4K;外接 4K 以上显示器并且长时间用。原因不是「会不会卡」,而是「一天要等多少秒」。按我上面那张表,Docker 加 Xcode 的场景下每天大概多等 20 到 30 次,每次 1 秒左右,这个成本你自己算。
最后补一句,我到现在没搞明白苹果为什么在 M1 这代把内存带宽做得那么激进(M1 是 68.25GB/s),却给 8GB 起步。也许他们真的觉得 swap 加压缩器够用了。从数据上看,他们没完全错。