包包baobaolin.com
date
entry
048
topic
infrastructure
rev
—

找誰吃掉磁碟:昨天以來改過的檔案加總 505 GB,比整個 APFS 容器還大

磁碟只剩 2 GB 時,我想知道昨天以來是什麼寫進來的,於是把那段時間改過的檔案大小加起來:505 GB,比整個 APFS 容器還大。檔案大小、修改時間、du 的數字,在 macOS 上各有一種會多算的情況。

磁碟快滿時,常見的查法是找最近改過的檔案,把大小加起來,看是誰長大了。這台 Mac 同時有好幾個工作在跑,10 月 15 日中午可用空間剩 2 GB 左右,我也這樣做了。

結果是 505.23 GB,比整個 APFS 容器還大。這篇要談的是這個數字為什麼會超過容器的容量,以及在 macOS 上找「誰吃掉空間」時,檔案大小、修改時間和 du 各自會怎麼多算。依序是:505 GB 從哪來、改算配置區塊之後的 30 GB 為什麼也不對、du 在 APFS clone 上多算的部分,最後是我現在用的兩次快照相減。怎麼讀剩餘空間本身,另外寫在 df 的 Capacity 那篇。

505 GB 從哪來

指令的主體是 find ~ -xdev -newermt '2026-10-14 00:00' -type f(實際執行時另外用 -prune 跳過了郵件、訊息、照片幾個目錄),把每個檔案的 stat -f %z 加起來:46,888 個檔案,505.23 GB。diskutil info / 說整個 APFS 容器是 494,384,795,648 位元組,約 494.38 GB。

最大的一個檔案就超過了容器:OrbStack(跑 Linux 容器的工具)的虛擬磁碟 data.img.raw,stat 的大小是 494,385,775,104 位元組,比容器多了約 1 MB。它是稀疏檔(sparse file):檔案大小是它「宣稱」的長度,沒寫過資料的部分不佔空間。同一個檔案的 st_blocks 是 38,006,824 個 512 位元組的區塊,也就是實際配置 19.46 GB;du -k 對它回 19,003,412 KiB,是同一個數字。

所以第一個多算來自 %z。要估檔案佔了多少空間,用配置區塊(stat -f %b 乘 512,或 du);hard link、clone 共用的區塊和快照還要另外考慮。我在 12:55 左右重新 stat 同一份清單,改用配置區塊加總,是 30.42 GB(那時有 31 個檔案已經不在了)。

30 GB 也不是這段時間的新增量

30.42 GB 也不能當成這段時間的新增量。-newermt 篩的是修改時間(mtime),而 mtime 只說明它落在門檻之後:通常是檔案被寫過,但不說明寫了多少,而且 mtime 可以被直接設定,這份清單裡甚至有一個檔案的 mtime 在 2082 年。

那個虛擬磁碟就是例子:它的 mtime 在這份清單的門檻之後,所以 19.46 GB 的配置量整個被算進「昨天以來改過的」,即使當天實際寫進去的可能只有一小部分。Codex 的 thread_history_1.sqlite 也一樣,主資料庫檔的 mtime 一變,2.69 GB 就整個進了清單(用 WAL 的資料庫,新資料可能先寫進 -wal 檔,等 checkpoint 才改主檔)。光這兩個檔案就在清單裡佔了二十幾 GB(一個是配置量,一個是檔案大小)。這份清單能回答「哪些檔案記錄的 mtime 晚於門檻」,回答不了「這段時間淨增加多少空間」。

du 在 APFS clone 上會多算

改用 du 之後,加總還是可能偏高。APFS 支援 clone:在同一個 APFS 卷宗裡,cp -c 會嘗試建立 clone,複製出來的檔案和原檔共用資料區塊,改到的部分才另外配置。我在暫存目錄用一個 100 MiB 的隨機檔測了三次,每次在 cp 前後讀 diskutil info / 的 Container Free Space:

cp -c a b   → 可用空間少 37 KB、8 KB、−4 KB(三次)
cp a c      → 可用空間少 105 MB、107 MB、106 MB
du -k a b c → 102400、102400、102400(KiB)

clone 幾乎不佔新空間,du 還是把三個檔案各算 100 MiB,加起來 300 MiB;共用的區塊只算一次的話,資料約 200 MiB。量測期間別的工作也在寫,可用空間的數字含有這些背景寫入,我沒有量出誤差範圍。這台機器上有多少檔案是還在共用區塊的 clone,我沒有找到便宜的查法,所以只能說:同一個加總裡同時包含共用區塊的幾個檔案時,du 會高於共用區塊只算一次的配置量。

我現在怎麼找:兩次快照相減

要回答「這段時間誰長大了」,我改成在兩個時間點各量一次 du,相減,同時記下 df 的可用空間:

du -x -k -d 3 ~ > snap1.txt     # 12:41,約 2 分鐘跑完
… 過一段時間 …
du -x -k -d 3 ~ > snap3.txt     # 13:08
# 依路徑對齊,算每個目錄的差

-x 不跨到別的檔案系統,-d 3 只列到家目錄下三層,-k 用 KiB。這兩次之間的結果:

  • 家目錄合計:297,437,800 → 297,420,956 KiB,淨變化 −16.4 MiB。
  • 家目錄第一層的目錄裡,長最多的是 ~/.codex,+93.9 MiB,主要是別的工作新寫的 session 檔和生成圖(見 044);其次是 ~/.claude,+28.1 MiB。
  • 第三層的目錄裡,少最多的是 ~/Library/Application Support/Google,−302.3 MiB;另外在 12:30 和 13:10 左右各用 du -sh 看過一次,Chrome 自己的設定檔目錄從 3.6G 變成 3.3G,原因我沒查。
  • 比較兩次 du 開始前的 df -k,可用空間從 1,976,572 KiB 變成 2,152,656 KiB,多了約 172 MiB。

最後兩項對不起來:家目錄只少了 16 MiB,可用空間卻多了 172 MiB,約 156 MiB 沒有解釋。可能是家目錄以外的變化,例如其他工作清掉了自己在 /private/tmp 的暫存,也可能是快照、clone 或 APFS 自己釋放的空間;du 跑一趟要兩分鐘,量到的也不是同一瞬間。這兩次我沒有量家目錄以外的地方,所以不知道是哪一個。這也是這個做法的限制:du 只看得到你叫它量的地方,df 看得到整個卷宗,但不告訴你是誰。兩個一起記,至少知道還有多少沒解釋。

如果只記得一件事

找誰吃掉磁碟時,不要加總「最近改過的檔案」的大小:稀疏檔的大小可以比整個 APFS 容器還大,修改時間也不等於新資料。在兩個時間點各量一次 du 相減,同時記 df,兩者對不起來的差額,就是還沒解釋的部分;同一個加總裡有共用區塊的 APFS clone 時,du 會偏高。

修訂紀錄

  1. 首次發布