macOS 15.5 / M4 Pro 24 GB RAM 環境で「スリープ解除直後にマウスも反応しない」ほど重くなる――。
原因は fseventsd のメモリリーク+Docker Desktop の常駐メモリ肥大 でした。
本記事では、再現方法・原因切り分け・恒久対策スクリプト・Docker 設定最適化までをやり切ったログを共有します。
調子こいて開発していたら、再びメモリリーク発生させてしまった。マシンスペックが高かったおかげで、挙動がカクつき始めた段階でトラブルシュートできた。主要因は私のPCの使い方にある。原因特定はあらかた済んだので、恒久的な対策を実装したら知見をまとめる。
— よだか(夜鷹/yodaka) (@yodakablog) July 2, 2025
1. イントロ――再発した“カクつき”症状と今回のゴール
- 現象
- 起床 → スクリーンセーバ解除直後、数十秒~数分間はレインボーカーソル。
- アクティビティモニタを見ると Swap 10 GB↑、CPU idle は 0〜10 %。
- 目的
- 原因を特定し、再発防止(自動回復)を仕込む
- 開発+仮想通貨 bot 常時運用 を 24 GB RAM 内で安定させる
2. まずは現状把握:vm_stat / top / log show で取れた数字
# メモリと Swap のスナップショット vm_stat; sysctl vm.swapusage # メモリ消費上位プロセス確認 top -l 1 -o mem -stats pid,command,mem # JetSam / GPU Process Kill などのシステムログ log show --last 1h --style syslog \ --predicate 'eventMessage CONTAINS "Jetsam" || eventMessage CONTAINS "GPU Process was killed"'
時点 | fseventsd RSS | Swap 使用量 | VirtualizationService | 備考 |
---|---|---|---|---|
発症直後 | 8 GB | 10 GB / 11 GB | 4 GB | Brave GPU Process Kill 多数 |
リセット後 | 20 MB | 0.4 GB | 0 GB | カクつき解消 |
3. 原因その①:fseventsd のメモリリーク を突き止める
3-1. 症状と再現条件
検証手順 | 結果 |
---|---|
bind-mount を大量に張ったコンテナを起動→Mac をスリープ | 復帰直後に fseventsd RSS が秒単位で増加 |
bind-mount なしで同一手順 | ほぼ増えない |
外部モニタを外した状態 | 変わらず再現 |
推測メカニズム
- Docker Desktop の gRPC FUSE 無効 + bind-mount が大量にある
- スリープ復帰 → 変更監視イベントが爆発 →
fseventsd
が処理し切れずリーク
3-2. 対策:watchdog スクリプト+cron で自動回収
/usr/local/sbin/fsevents_watchdog.sh
#!/bin/zsh # fseventsd が 1 GB を超えたら強制終了し、launchd の自動再起動に任せる THRESHOLD_KB=1048576 PID=$(pgrep fseventsd) || exit 0 RSS_KB=$(ps -o rss= -p "$PID" | tr -d ' ') if [[ "$RSS_KB" -ge "$THRESHOLD_KB" ]]; then logger -t fsevents_watchdog "RSS ${RSS_KB}KB exceeds ${THRESHOLD_KB}KB → pkill" /usr/bin/pkill -9 fseventsd fi
# 設置と権限付与 sudo install -m 755 fsevents_watchdog.sh /usr/local/sbin/ # root の cron に 15 分間隔で登録 sudo crontab -e */15 * * * * /usr/local/sbin/fsevents_watchdog.sh
ログ確認:
log show --last 15m --predicate 'eventMessage CONTAINS "fsevents_watchdog"'
4. 原因その②:Docker Desktop が食う常駐メモリ
4-1. Virtualization.framework を切る
Before | After |
---|---|
com.apple.VirtualizationService 4 GB 常駐 | 0 GB |
Preferences ▸ General ▸ **Use virtualization framework**
を OFF にするだけで OK。
M4/M3 世代 Mac では Apple Hypervisor に戻す方が軽いケース多し。
4-2. 「場外 4 GB 枠」に絞る GUI 設定
メニュー | 値 | 理由 |
---|---|---|
General ▸ Start Docker… | OFF | Mac 起動直後は Docker が立ち上がらない |
Resources ▸ CPUs 4 / Memory 4 GB | bot 群+IDE で余裕/Swap が増えたら 6 GB へ | |
Resources ▸ Swap 1 GB | bot 暴走時の逃げ道 | |
Advanced ▸ gRPC FUSE ON | bind-mount の FSEvents 激減 | |
File Sharing | 必要最小限 | fseventsd の観測対象を絞る |
4-3. Compose 側でコンテナ上限を設定
services: bot: image: mybot:latest mem_limit: 768m memswap_limit: 1g db: image: postgres:16 mem_limit: 1.5g
5. 追加チューニング――Electron/Slack/Brave の負荷軽減
- Hardware-accelerated canvas を OFF
- Brave / Slack を Universal 版 → Apple Silicon 版へ統一
- Mission Control「モニタごとに個別スペース」設定の見直し(外部モニタ多用時のみ)
6. 試運転フェーズ:1 週間モニタリング 指標
項目 | 目安値 | コマンド |
---|---|---|
Docker VM 使用メモリ | ≤ 80 % | docker stats --no-stream |
macOS Swap | ≤ 0.5 GB | vm_stat; sysctl vm.swapusage |
fseventsd RSS | ≤ 20 MB | top -pid $(pgrep fseventsd) |
CPU 温度 | Idle 40–50 ℃ | sudo powermetrics --samplers smc -n 1 |
7. 結果と学び(観測中)
- watchdog + cron 導入後、fseventsd が 100 MB を超えていない
- Docker VM 4 GB でも bot 群・VS Code(Cursor)・Browser を同時稼働して Swap 0.3 GB 前後
- 今後は GUI コンテナ(Grafana など)追加時のメモリ再見積もりが課題
8. まとめ
リーク元プロセスを特定して自動回収
ホストと VM のメモリ上限を揃えるたった 2 つの方針で「開発 × 常時運用」の両立が可能になりました。
同じ症状で悩む macOS + Docker Desktop ユーザの参考になれば幸いです!
付録:よくある Q&A
疑問 | 回答 |
---|---|
watchdog が動いているか確認したい | log show --last 1h --predicate 'eventMessage CONTAINS "fsevents_watchdog"' |
cron が正しく動作しない | root の crontab かどうか / 改行コード / ファイルの改行終端を確認 |
Docker 起動時だけ VirtualizationService が現れる | 正常。常駐していなければ設定は反映済 |

これで明日からも快適に開発&bot運用ができる,,,はず!
【Macで仮想通貨bot開発するってどうなの?】
✅ 向いてる点
UNIXベース:bash/zsh、python、Node、Docker、Gitなどがデフォルトで使いやすい。Apple Silicon:高効率&爆速(ただし最適化されてるソフトに限る)。
開発者向けGUI環境:VS CodeやCursor、Postman、ターミナルツール、全部揃ってる。
— よだか(夜鷹/yodaka) (@yodakablog) July 2, 2025