Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1460] ファイルの概要

このコミットは、Go言語のランタイムにおけるメモリ管理の一部である src/runtime/malloc.c ファイルに対する変更です。具体的には、free 関数が nil (C言語における NULL に相当) ポインタを受け取った際の挙動を修正しています。

コミット

commit 0c3243053465e0e4482fb5040c2e4e16972751cc
Author: Russ Cox <rsc@golang.org>
Date:   Fri Jan 9 16:22:13 2009 -0800

    free(nil)

    R=iant
    DELTA=3  (3 added, 0 deleted, 0 changed)
    OCL=22467
    CL=22471

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/0c3243053465e0e4482fb5040c2e4e16972751cc

元コミット内容

このコミットの目的は、「free(nil)」の挙動を修正することです。これは、Goランタイムのメモリ解放関数 freenil ポインタを引数として受け取った場合に、何もせずに即座にリターンするように変更することを意味します。

変更の背景

C言語の標準ライブラリ関数 free() は、NULL ポインタを引数として受け取った場合、何もしないことが保証されています。これは、メモリ解放処理を行う前にポインタが有効かどうかをチェックする手間を省き、コードを簡潔にするための一般的な慣習です。

Go言語の初期のランタイムにおける free 関数は、nil ポインタが渡された場合に、内部で不正なメモリアクセスやクラッシュを引き起こす可能性がありました。このコミットは、Goランタイムのメモリ管理をより堅牢にし、C言語の free 関数と同様の安全な挙動を保証するために導入されました。これにより、プログラマが free を呼び出す前にポインタが nil でないことを明示的にチェックする必要がなくなり、Goのメモリ管理の信頼性が向上しました。

前提知識の解説

メモリ管理と free 関数

コンピュータプログラムは、実行時にメモリを動的に確保し、使用し、そして解放する必要があります。このプロセスをメモリ管理と呼びます。malloc (またはGoにおける newmake に相当する内部的なアロケータ) はメモリを確保し、free は確保されたメモリをシステムに返却する役割を担います。メモリを解放し忘れるとメモリリークが発生し、確保されていないメモリを解放しようとすると未定義の動作やクラッシュを引き起こす可能性があります。

ポインタと nil

ポインタは、メモリ上の特定のアドレスを指し示す変数です。nil (C言語では NULL) は、どの有効なメモリ位置も指していないことを示す特別なポインタ値です。プログラムにおいて、ポインタが nil であるかどうかをチェックすることは、無効なメモリアクセスを防ぐ上で非常に重要です。

Goランタイム

Go言語は、ガベージコレクションを備えた独自のランタイムを持っています。このランタイムは、メモリの確保、解放、スケジューリング、ゴルーチンの管理など、Goプログラムの実行に必要な低レベルの操作を処理します。src/runtime/malloc.c は、このランタイムの一部であり、Goプログラムが使用するメモリの動的な割り当てと解放を担当するC言語で書かれたコードです。

技術的詳細

このコミットの技術的な詳細は、free 関数が nil ポインタを安全に処理するように変更された点に集約されます。

従来の free 関数は、引数 v (解放するメモリブロックへのポインタ) が nil であることを想定していませんでした。そのため、vnil の場合、その後の処理(例えば、page = (uintptr)v >> PageShift; のようなアドレス計算や、MHeapMapCache_GET を用いたメモリヒープの検索)が不正なメモリアドレスに対して行われ、ランタイムエラーやセグメンテーション違反を引き起こす可能性がありました。

この変更により、free 関数の冒頭で if(v == nil) return; というシンプルなチェックが追加されました。これにより、vnil であった場合、関数は即座に処理を終了し、それ以降の危険な操作が実行されるのを防ぎます。これは、防御的プログラミングの典型的な例であり、関数の入力に対する堅牢性を高めます。

Go言語のランタイムはC言語で書かれている部分が多く、C言語の慣習(free(NULL) が安全であること)に合わせることで、Goのメモリ管理コードの移植性、保守性、そして安全性が向上します。

コアとなるコードの変更箇所

変更は src/runtime/malloc.c ファイルの free 関数内で行われました。

--- a/src/runtime/malloc.c
+++ b/src/runtime/malloc.c
@@ -68,6 +68,9 @@ free(void *v)
 	MSpan *s;
 	MCache *c;

+	if(v == nil)
+		return;
+
 	// Find size class for v.
 	page = (uintptr)v >> PageShift;
 	sizeclass = MHeapMapCache_GET(&mheap.mapcache, page, tmp);

コアとなるコードの解説

追加された3行のコードは以下の通りです。

	if(v == nil)
		return;
  • if(v == nil): これは条件分岐の開始です。vfree 関数に渡されたポインタです。nil はGoランタイムのCコードにおける NULL に相当するマクロまたは定義です。この条件は、「もしポインタ vnil であるならば」という意味になります。
  • return;: これは、if 文の条件が真(つまり vnil)であった場合に実行されるステートメントです。関数から即座に制御を戻し、それ以降の free 関数の処理(メモリ解放ロジック)を実行しないようにします。

このシンプルな変更により、free 関数は nil ポインタに対して安全な操作となり、Goプログラムの安定性が向上しました。

関連リンク

参考にした情報源リンク

  • 上記のGitHubコミットページ: https://github.com/golang/go/commit/0c3243053465e0e4482fb5040c2e4e16972751cc
  • Go言語のランタイムに関する一般的な知識 (Goのメモリ管理、ガベージコレクションなどに関する記事や書籍)
  • C言語のメモリ管理 (malloc, free) に関する一般的な知識 (C言語の標準ライブラリ関数に関するドキュメント)
  • 防御的プログラミングの原則に関する情報