エラーとデバッグ — Segfault・StackOverflow・OOM・デッドロック
エラーメッセージを読むと、これまでの章で学んだCS基礎の単語がそのまま登場します。「stack size exceeded」「out of memory」「connection refused」などはすべて、コンピュータの仕組みを知っていればどこで何が起きているか を想像できる言葉です。
この本の最後の章では、実務でよく遭遇する代表的なエラーを、原因と対処 がセットで頭に入る形で整理します。
エラーが起きている「場所」を先に見分ける
Webアプリのエラーは、おおまかにどの層で起きたか を切り分けるところから始めます。
どの層で起きているかで読み解き方と直し方が違う ので、エラーメッセージを見たら最初に「これはどの層の話だ?」と分類する癖をつけます。
Segmentation Fault — 触ってはいけないメモリを触った
Segmentation fault(通称 segfault / セグフォ)は、プログラムがアクセスを許可されていないメモリ領域 にアクセスしようとしたときに発生します。
$ ./my_program
Segmentation fault (core dumped)
典型的な原因は以下です。
- NULLポインタの参照 — 初期化していないポインタを使った
- 解放済みメモリへのアクセス —
free()済みのメモリを使った - 配列の範囲外アクセス — 境界を超えたインデックス
CやC++のような手動でメモリ管理する言語 でよく出ます。JavaScriptやPythonでは、ランタイムがガベージコレクションで守ってくれるので、直接遭遇するのはネイティブモジュールの不具合やランタイム自体のバグ がほとんどです。
StackOverflow — コールスタックが溢れた
StackOverflowError は、コールスタック が上限を超えたときに発生します。第2章のスタック(LIFO)がそのままイメージの中心です。
最も多いのは終了条件のない再帰呼び出し です。
function countdown(n) {
return countdown(n - 1);
}
countdown(10);
// → RangeError: Maximum call stack size exceeded直し方はシンプルで、ベースケース(終了条件)を書く だけです。
function countdown(n) {
if (n <= 0) return;
console.log(n);
return countdown(n - 1);
}「再帰で書いたら StackOverflow が出た」というときは、たいてい終了条件が想定外のパスで発動しない(配列が空のとき、null が来たとき、循環参照のデータが来たとき)のが原因です。
Out of Memory (OOM) — メモリが尽きた
OOMエラーは、プログラムが使用可能なメモリを使い切った ときに発生します。
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed
- JavaScript heap out of memory
主な原因は3つです。
- メモリリーク — 不要になったオブジェクトへの参照が残り続け、GCで回収されない
- 巨大データの一括読み込み — 数GBのファイルやDB結果をそのままメモリに載せた
- 無限に溜め込む処理 — ループの中で配列に追加し続ける、キャッシュに期限を設定しない、など
メモリリークの典型
const cache = [];
function handleRequest(req) {
cache.push(req);
// 取り出す側がいない → 永遠に増える
}こうしたコードは、ローカルでは動くが本番で数時間〜数日後に落ちる という質の悪いバグになります。
対処の基本
「メモリを増やす」は最後の手段 です。根本の原因(リーク・設計)を放置すると、増やした分を消費するまでの時間が延びるだけで、いずれ再発します。
- Node.js なら
--max-old-space-size=4096のような指定で一時的に回避 - 大きなファイル はストリームで流す(
fs.createReadStream) - DB結果 はページングやカーソルで分割する
- キャッシュ はTTLや上限サイズを設ける(LRUキャッシュなど)
デッドロック — 互いのロックを待ち続けて止まる
デッドロック は、2つ以上のスレッドが互いの持っているリソースを待ち続けて、どちらも進めなくなる 状態です。
デッドロックが発生するには、以下の4条件がすべて同時に満たされる 必要があります。
- 相互排他 — リソースは一度に1つしか使えない
- 保持と待機 — あるリソースを持ったまま、別のリソースを待つ
- 横取り不可 — 他のスレッドから強制的に奪えない
- 循環待ち — A→B→A のように待ちが環状になっている
4つめの 循環待ち を崩すのが最も実践的です。ロックを取る順番をシステム全体で統一 すれば、循環ができなくなります。
Web開発で直接スレッドを扱うことは少ないですが、DBのトランザクション では普通に発生します。「同じ順序で行をUPDATEする 」「トランザクションを短く保つ 」「タイムアウトを設定する」というのが基本の対策です。
Connection Refused — 接続先が受け付けてくれない
Connection refused は、クライアントがサーバーに接続しようとしたが、明示的に拒否された エラーです。
Error: connect ECONNREFUSED 127.0.0.1:3000
原因は多くの場合、次のいずれかです。
- サーバーが起動していない — 本命。まずはプロセスが動いているか確認
- ポート番号が違う —
.envのDB_PORTやAPI_URLを確認 - バインドしているアドレスが違う —
0.0.0.0でなく127.0.0.1で起動していてコンテナ外から見えない、など - ファイアウォール / セキュリティグループで遮断 — AWSなどで SG のインバウンドルールが足りていない
切り分けの順序は、前章でも触れたとおり ping → nslookup → curl -v でどの層で失敗するか を確認する流れが最短です。
エラーに向き合うときの型
どんなエラーでも、対処の基本姿勢は共通です。
- エラーメッセージを最後まで読む スタックトレースの最下層 ではなく、自分のコードに最も近い行 を探します
- 再現条件を特定する 必ず出るのか、特定の入力だけで出るのか、時間が経つと出るのか
- 直前の変更を疑う
git logやgit diffで、エラーが出始めたタイミングの変更を確認 - そのままメッセージで検索 固有の値(ファイルパス、ID等)を消した形で検索すると、同じエラーに遭った人の記事が見つかります
- 最小再現コードを作る 他人に聞く前の最後の一手。たいていの場合、この過程で自分で原因に気づきます
ちゃんと使うためのポイント
- エラーを見たら、まずどの層で起きたか(フロント / アプリ / ランタイム / インフラ)を切り分ける
- Segfault は主にCやネイティブの世界。NULL・解放済みメモリ・配列外アクセス が三大原因
- StackOverflow は再帰の ベースケース漏れ を疑う
- OOM はメモリを増やす前にリークか巨大データ処理 を疑う。ストリームとページングが対処の基本
- デッドロック はDBトランザクションで身近に起きる。ロック順を揃える・短く終わらせる
- Connection refused はまずプロセス起動・ポート・バインドアドレス を確認
最後に
この本で扱ったのは以下の内容でした。
- コンピュータの仕組み — CPU・メモリ階層・プロセス/スレッド・仮想メモリ
- データ構造 — 配列・スタック・キュー・連結リスト・ハッシュテーブル
- アルゴリズムと計算量 — Big-O・線形/二分探索・代表的なソート
- ネットワーク基礎 — IP・DNS・TCP/UDP・HTTP
- エラーとデバッグ — Segfault・StackOverflow・OOM・デッドロック・Connection refused
これらはフレームワークが変わっても、言語が変わっても、残り続ける土台の知識 です。明日すぐ書くコードを変えるわけではないかもしれませんが、パフォーマンスの勘所、障害対応、アーキテクチャ選定 の判断力として、長く効いてきます。
全部を完璧に覚える必要はありません。「この単語を見たことがある」「何を調べればいいか分かる」 という状態になっていれば、実務でハマったときに一気に調べ進められるようになります。
あとは、ウェブエンジニア問題集のクイズで繰り返し確認して、知識を定着させていきましょう。