第四種降低處理複雜性的技巧:對某些錯誤,乾脆當掉應用程式。
適用情境#
某些錯誤滿足三個條件 → 嘗試處理只是徒勞:
- 處理起來困難或根本不可能
- 發生頻率低
- 處理方案幾乎都會失敗
最簡單的回應:印診斷資訊 → abort。
經典範例:記憶體耗盡#
C 的 malloc#
malloc失敗時回傳NULL- 假定每個呼叫端都會檢查回傳值並妥善處理
問題:
- 應用內
malloc呼叫極多 → 每次都檢查會把程式碼複雜度推到失控 - 一旦忘記檢查(很容易發生)→ 解 null pointer → 當掉,還掩蓋了真正的問題
且應用本身也做不了什麼:
- 「找一些不需要的記憶體釋放掉」?如果真有不需要的,早就該釋放了
- 現代系統記憶體大到極少耗盡;真的耗盡通常代表 bug
較好的做法:ckalloc#
void* ckalloc(size_t n) {
void* p = malloc(n);
if (p == NULL) {
// 印錯誤訊息 → abort
}
return p;
}應用程式永遠不直接呼叫 malloc,全部走 ckalloc。
C++ / Java 的 new#
- 失敗時拋例外
- 接住例外幾乎沒意義:handler 多半也要分配記憶體 → 又會失敗
- 動態記憶體在現代應用如此基本,記憶體耗盡時應用無法繼續——當掉是正解
其他適合「當掉」的情境#
- 對既開檔案進行 I/O 時遇到 disk hard error
- 開不到網路 socket
- 偵測到內部資料結構不一致 → 可能是 bug
這類錯誤通常罕見,當掉並印明確錯誤訊息不影響整體可用性。
例外:當掉並非永遠合適#
「該不該當掉」取決於應用。
例如:複本式儲存系統不能因為 I/O 錯誤就 abort——它必須用副本資料復原遺失的資訊。
復原機制會引入大量複雜性,但復原資料正是這類系統提供的核心價值。