第四種降低處理複雜性的技巧:對某些錯誤,乾脆當掉應用程式

適用情境#

某些錯誤滿足三個條件 → 嘗試處理只是徒勞:

  • 處理起來困難或根本不可能
  • 發生頻率低
  • 處理方案幾乎都會失敗

最簡單的回應:印診斷資訊 → 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——它必須用副本資料復原遺失的資訊。

復原機制會引入大量複雜性,但復原資料正是這類系統提供的核心價值