Hands-on:LLM 運行中 + 結束的資源管理
跑本地 LLM 的核心 invariant 跟雲端不一樣:Mac 是 shared resource、不是 dedicated GPU。雲端 inference server 跑進 dedicated container、結束 instance 自然回收所有資源;本地推論伺服器跑在你日常用的 Mac、跟 統一記憶體 共享同一塊容量,忘記管理會 silently 吃光 RAM、磁碟、port、最後讓系統變慢甚至 swap。
本篇紀錄三個 dimension(RAM / 磁碟 / port)的觀察工具跟釋放姿勢、對比 Ollama 跟 ComfyUI 兩種典型 lifecycle、加上實測釋放數字。對應 0.7 隱私資料流原理「每個 hop 都要 audit」這條思維——資源管理也是 hop 級的 audit、不是「裝完就忘」。
驗證日期:2026-05-12 環境:macOS 14、Apple Silicon、Ollama 0.23.2、ComfyUI 0.21.0、SDXL base 1.0
為什麼這事重要
雲端 inference:
1Container start → load model → serve requests → container stop → 所有 RAM / 磁碟 / port 自動回收本地 inference:
1brew services start → load model on demand → serve → ??? → 你忘記 stop
2 → RAM / 磁碟一直被佔
3 → 下次重開機才釋放具體會踩到的問題:
- RAM:18 GB SDXL 模型載入後不會自動卸、即使 ComfyUI idle、Python process 仍占 RAM
- 磁碟:
ollama pull累積、~/.ollama/models/blobs半年可長到 50 GB+、不主動清不會減 - Port:上次 crash 的
ollama serve進程沒乾淨清、port 11434 還占著、下次啟動報「address already in use」 - GPU / Metal:模型載入後 Metal context 佔住、跟其他 GPU-using app(影片剪輯、遊戲)競爭
三個 dimension + 觀察工具
| Dimension | 觀察指令 | 看什麼 |
|---|---|---|
| RAM | vm_stat | head -5 | Pages free(每 page 16 KB)、空閒越多越好 |
| RAM(per process) | Activity Monitor 或 ps aux | sort -k6 -rn | head | 哪個 process 佔最多記憶體 |
| 磁碟 | df -h ~ | tail -1 | 系統 volume 剩餘 |
| 磁碟(per dir) | du -sh ~/.ollama/models/blobs | LLM models 累積量 |
| Port | lsof -i :11434 | 誰在 listen 該 port |
| Process | ps aux | grep -i ollama | grep -v grep | Ollama / ComfyUI / Python 跑哪幾個 |
| Ollama loaded models | ollama ps | 哪些 model 在 RAM、size、idle timer |
實測:剛 kill 完 ComfyUI(SDXL + Python venv)後、vm_stat 看到 free pages 從 619K 變 1090K(每 page 16 KB)、約 +7.5 GB RAM 釋放——這就是 SDXL + ComfyUI process 一直占的記憶體量。
Ollama 的 lifecycle(auto-unload 模式)
Ollama 走「按需 load / idle unload」設計:
1brew services start ollama → daemon 啟動、沒 model 載入、RAM 占用 ~200 MB
2 port 11434 listening
3ollama run gemma3:4b "hello" → 把 model 載入 RAM (~4-5 GB)
4 立刻 generate response
5 model 留在 RAM
6(idle 5 分鐘、無新 request) → Ollama 自動 unload model
7 RAM 釋放、daemon 仍跑著
8ollama run gemma3:4b "next" → 重新 load model(~5-10 秒)、generate
9brew services stop ollama → daemon 結束、port 釋放關鍵參數 OLLAMA_KEEP_ALIVE(環境變數、預設 5m):
1# 看當前 loaded models
2ollama ps
3# NAME ID SIZE PROCESSOR UNTIL
4# gemma3:4b a2af6cc3eb7f 5.5 GB 100% Metal 4 minutes from now
5
6# 啟動時調 keep_alive(持續佔 RAM 直到 ollama 重啟)
7OLLAMA_KEEP_ALIVE=-1 brew services restart ollama
8
9# 啟動時讓 model 用完立即 unload
10OLLAMA_KEEP_ALIVE=0 brew services restart ollama選 keep_alive 的 trade-off:
| 設定 | RAM 占用 | 首字延遲 | 適合場景 |
|---|---|---|---|
0 | 最低(generate 完立即釋放) | 高(每次都重 load) | 偶爾用、RAM 緊張 |
5m(預設) | 中(活躍用占住、閒 5 分鐘後釋放) | 低(活躍期不重 load) | 大多場景 |
-1 | 高(永久占住) | 最低 | 整天頻繁用、RAM 充裕 |
主動 unload 指令:
1# 把 idle 的 model 立刻從 RAM 卸掉、但 daemon 仍跑
2curl -s http://localhost:11434/api/generate \
3 -d '{"model": "gemma3:4b", "keep_alive": 0}'
4
5# 或關掉整個 daemon
6brew services stop ollamaComfyUI 的 lifecycle(持續占用模式)
ComfyUI 走完全不同模式:model 載入後一直在 RAM、直到 server process 結束。沒有 auto-unload 機制。
1python main.py → ComfyUI server start、port 8188 listening
2 RAM ~3 GB(Python venv + 框架)
3第一次 Queue Prompt (用 SDXL) → 載入 sd_xl_base_1.0.safetensors (~6 GB)
4 RAM 跳到 ~9-10 GB
5 generate 完成、model 留在 RAM
6連續多張生成 → 維持 ~9-10 GB、沒 unload
7idle 1 小時 → 仍 ~9-10 GB(沒 timer)
8切到 ControlNet workflow → 多載 ControlNet model (~2 GB)、ComfyUI 自動 swap
9 RAM 暫升、SD 部分可能被 evict 到 disk
10Ctrl+C / pkill → process 結束、RAM 完全釋放要釋放 ComfyUI 占的 RAM、唯一方法是結束 server:
1# 找 PID
2ps aux | grep "ComfyUI/main.py" | grep -v grep
3
4# 優雅關(讓它 cleanup)
5pkill -INT -f "ComfyUI/main.py"
6
7# 強制 kill(如果上面沒反應、最多等 5 秒再強制)
8pkill -KILL -f "ComfyUI/main.py"
9
10# 確認 port 釋放
11lsof -i :8188 | head -3實測:M4 Pro 32GB、SDXL base 載入後 ComfyUI process 占 ~8 GB RAM;pkill -9 後 vm_stat 顯示 free pages 增加 ~470K page(7.5 GB 釋放)。
為什麼 Ollama 跟 ComfyUI 設計不同
| 因素 | Ollama 設計 | ComfyUI 設計 |
|---|---|---|
| 主要使用模式 | API 服務、IDE plugin 透過 HTTP 用 | 互動 GUI、user 連續調 prompt |
| Model 切換頻率 | 高(不同任務換不同 model) | 低(一次 session 通常一個 model) |
| User 期待的 latency | 低首字延遲(IDE 補完場景) | 高 throughput(連續生圖) |
| 結論 | Auto-unload 釋 RAM 給其他 model | 持續載入避免重複 load 浪費 |
兩種設計都 valid、適合不同使用模式。理解差異後就知道 ComfyUI 一直占 RAM「不是 bug」、是設計選擇。
跟其他本地 server 對比
| Server | Auto-unload | 主動 unload 指令 | 占 RAM 觀察 |
|---|---|---|---|
| Ollama | 有(5 分鐘 idle) | keep_alive: 0 或 stop daemon | ollama ps |
| LM Studio | 無(GUI 主動關閉 model 才釋) | GUI Eject Model | Activity Monitor |
llama.cpp llama-server | 無 | kill process | lsof -i :8080 |
| ComfyUI | 無 | kill process | ps aux | grep ComfyUI |
| oMLX | 有(per model 可配) | API endpoint | server log |
結論:只有 Ollama 跟 oMLX 內建 auto-unload、其他都要手動釋放。GUI server(LM Studio)通常給 user 一個「Eject」按鈕、CLI server 通常要 kill process。
標準釋放程序
寫 code 完一天結束、要釋放所有資源、按下表順序操作:
1# 1. 確認當前狀態(記下要還回去多少 RAM)
2vm_stat | head -3
3df -h ~ | tail -1
4ollama ps
5ps aux | grep -E "ollama|ComfyUI|llama-server" | grep -v grep
6
7# 2. 釋放當前載入的 LLM models(Ollama)
8brew services stop ollama
9# 或保留 daemon、只 unload model:
10# curl -s http://localhost:11434/api/generate -d '{"model": "<your model>", "keep_alive": 0}'
11
12# 3. 結束 ComfyUI / 其他 GUI server
13pkill -INT -f "ComfyUI/main.py" 2>/dev/null
14pkill -INT -f "llama-server" 2>/dev/null
15sleep 5
16# 強制(如果上面沒清乾淨)
17pkill -KILL -f "ComfyUI/main.py" 2>/dev/null
18pkill -KILL -f "llama-server" 2>/dev/null
19
20# 4. 驗證所有 port 釋放
21lsof -i :11434 -i :1234 -i :8080 -i :8188 -i :8000 2>&1 | head
22
23# 5. 確認釋放量
24vm_stat | head -3
25# free pages 該明顯增加容易出錯的「釋放方式」
killall Python:會 kill 所有 Python process、包括其他 dev tool(如 jupyter、Django)。用pkill -f "ComfyUI/main.py"等明確 pattern。rm -rf ~/.ollama:會清掉所有 model registry、下次要重 pull 全部 model。Cleanup 用ollama rm <model>才精準。brew uninstall ollama:直接卸載 Ollama 本身、過 reinstall 麻煩。Stop service 就夠。- 重開機釋放:work 但太重、會中斷其他工作。用 process-level 操作即可。
磁碟長期累積管理
Models 一旦 pull 進 ~/.ollama/models/blobs、不主動 rm 不會減少。半年累積可長到 50 GB+。
Ollama models 只是磁碟大戶之一。整台 Mac 突然被吃光、要從哪裡查起的全機診斷順序(先排除快照浮動、再用實際佔用值逐層找大戶),見 macOS 磁碟空間診斷流程——那篇的佔用大戶表也會把 ollama 列為其中一項、再連回本篇的專屬清理 idiom。
觀察累積
1# Ollama models 總占用
2du -sh ~/.ollama/models/blobs
3# 4.1G /Users/tarragon/.ollama/models/blobs
4
5# 逐 model 看大小
6ollama list
7# NAME ID SIZE MODIFIED
8# gemma4:e4b c6eb396dbd59 9.6 GB Less than a second ago
9# nomic-embed-text:latest 0a109f422b47 274 MB 3 hours ago
10
11# ComfyUI checkpoints 累積
12du -sh ~/.ollama ~/Projects/ComfyUI/models 2>/dev/null
13# 4.2G /Users/tarragon/.ollama
14# 7.0G /Users/tarragon/Projects/ComfyUI/models清理策略
1# 刪掉很久沒用的 model
2ollama rm <model-tag>
3
4# 一次清掉所有 Ollama models(保留 daemon)
5ollama list | tail -n +2 | awk '{print $1}' | xargs -I {} ollama rm {}
6
7# 看 ComfyUI checkpoints 哪些可清
8ls -lh ~/Projects/ComfyUI/models/checkpoints/
9
10# 手動刪不要的 .safetensors(小心、不能 undo)
11rm ~/Projects/ComfyUI/models/checkpoints/<old-model>.safetensors磁碟管理 idiom
定期(每月或磁碟剩 < 20% 時)做:
du -sh ~/.ollama ~/Projects/ComfyUI/models看當前累積ollama list看哪些 model 沒在用(看MODIFIED欄、太舊的考慮刪)- 刪實驗用的 model、保留 daily-driver
- ComfyUI checkpoints 同樣 review
Port / Process 排錯
啟動報「address already in use」
1# 找誰占
2lsof -i :11434
3# COMMAND PID USER ... NAME
4# ollama xxx ... ... TCP localhost:11434 (LISTEN)
5
6# 看是不是 zombie process
7ps aux | grep $(lsof -ti :11434 | head -1)
8
9# 清掉
10kill -9 $(lsof -ti :11434)
11
12# 或重啟 service(會自動清舊 instance)
13brew services restart ollamaOllama daemon 掛了不知道
1# 健康檢查
2curl -s http://localhost:11434/api/version
3
4# 沒回應、看 service 狀態
5brew services list | grep ollama
6
7# 沒在跑、重啟
8brew services start ollama
9
10# 看 log
11tail -50 /opt/homebrew/var/log/ollama.logComfyUI 看似跑著但 Queue 不動
1# 看 stdout / stderr log
2tail -30 /tmp/comfyui.log # 如果啟動時 redirect 到 log
3
4# 看是不是 GPU / Metal stuck(極少見、但 SDXL 大量並發可能踩到)
5# 解法:kill + 重啟
6pkill -9 -f "ComfyUI/main.py"完整排錯流程跟「先確認哪一層壞」見 1.7 排錯方法論。
觀察記憶體佔用:實測對照
跑這幾步紀錄 baseline → load model → kill 的 RAM 變化:
1# Baseline
2vm_stat | grep "Pages free"
3# Pages free: 1090076. ← ~17 GB free
4
5# 啟動 Ollama + load 4B model
6brew services start ollama
7ollama run gemma3:4b "hello"
8ollama ps
9# NAME SIZE PROCESSOR UNTIL
10# gemma3:4b 5.5 GB 100% Metal 4 minutes from now
11
12vm_stat | grep "Pages free"
13# Pages free: 750000. ← 跌 ~5 GB(model 載入)
14
15# 額外啟動 ComfyUI + load SDXL
16nohup python main.py > /tmp/comfyui.log 2>&1 &
17# 在 GUI 上 Queue Prompt 跑一次 SDXL generation
18vm_stat | grep "Pages free"
19# Pages free: 280000. ← 再跌 ~7.5 GB(SDXL 載入 + Python venv)
20
21# kill 全部
22brew services stop ollama
23pkill -9 -f "ComfyUI/main.py"
24sleep 3
25vm_stat | grep "Pages free"
26# Pages free: 1090000. ← 回到 baseline每 page 16 KB、所以 free pages 數字 × 16 KB = 實際 free RAM bytes。
自動化釋放:launchd / shell alias
寫個 shell function 一鍵 cleanup:
1# 加進 ~/.zshrc
2llm-cleanup() {
3 echo "[*] Stopping Ollama..."
4 brew services stop ollama 2>/dev/null
5
6 echo "[*] Killing ComfyUI..."
7 pkill -INT -f "ComfyUI/main.py" 2>/dev/null
8 sleep 3
9 pkill -KILL -f "ComfyUI/main.py" 2>/dev/null
10
11 echo "[*] Killing other model servers..."
12 pkill -KILL -f "llama-server" 2>/dev/null
13 pkill -KILL -f "lm-studio-server" 2>/dev/null
14
15 echo "[*] Verifying ports..."
16 for p in 11434 1234 8080 8188 8000; do
17 lsof -i :$p 2>/dev/null | head -2
18 done
19
20 echo "[*] Free RAM:"
21 vm_stat | grep "Pages free"
22}完事打 llm-cleanup 一鍵釋放、不用記每個 process 怎麼 kill。
何時這篇會過時
不會過時的部分:
- RAM / 磁碟 / port 三個 dimension 是長期 invariant、用什麼 LLM server 都成立。
- 「Mac 是 shared resource、需要主動管理」這個 framing。
- Ollama 跟 ComfyUI 兩種典型 lifecycle 對比(auto-unload vs persistent)。
- 觀察工具(
vm_stat、lsof、ps、du、Activity Monitor)是 macOS 系統 API、不會 deprecate。 - 標準釋放程序、自動化 shell function 模式。
會變的部分:
- 具體 model size / RAM 占用數字(隨模型架構演化)。
OLLAMA_KEEP_ALIVE等具體環境變數名(Ollama API 演化)。- ComfyUI 可能加 auto-unload feature(社群有 issue 在討論)。
讀的時候若指令跑不過、先 --help 看當前版本 flag;釋放 RAM 的「kill process」這個機制本身永遠成立。
跟其他 hands-on 章節的關係
- Ollama 安裝:介紹
brew services start/stop、本篇延伸 lifecycle 細節 - ComfyUI 安裝:介紹 ComfyUI 啟動、本篇延伸 RAM 占用 + 釋放
- 1.7 排錯方法論:用三層架構定位故障、本篇是 lifecycle 視角的補完
- 0.7 隱私資料流原理:「每個 hop 都要 audit」延伸到資源層
整體心法:本地 LLM 工作流跟雲端不一樣、要主動管理 lifecycle、不能裝完就忘。