[インデックス 11744] ファイルの概要
このコミットは、Go言語のディストリビューションツール (cmd/dist
) におけるWindows環境でのメモリ再割り当て処理に関する変更です。具体的には、HeapReAlloc
関数の呼び出しから HEAP_GENERATE_EXCEPTIONS
フラグを削除しています。
コミット
commit eaf640dbc41ab96dbae5b55708b2e42eec22fd53
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Fri Feb 10 09:14:00 2012 +1100
cmd/dist: do not use HEAP_GENERATE_EXCEPTIONS flag
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5650048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/eaf640dbc41ab96dbae5b55708b2e42eec22fd53
元コミット内容
cmd/dist: do not use HEAP_GENERATE_EXCEPTIONS flag
変更の背景
このコミットは、Go言語のビルドシステムの一部である cmd/dist
がWindows環境でメモリを管理する方法を修正しています。cmd/dist
は、Goのソースコードから実行可能ファイルをビルドするために使用されるツールです。Windowsでは、メモリ管理にWin32 APIのヒープ関数(HeapAlloc
, HeapReAlloc
など)が使用されます。
HeapReAlloc
関数は、既存のメモリブロックのサイズを変更するために使用されます。通常、この関数が失敗した場合(例えば、メモリ不足の場合)、NULL
を返します。しかし、HEAP_GENERATE_EXCEPTIONS
フラグを指定すると、失敗時に NULL
を返す代わりに、構造化例外(STATUS_NO_MEMORY
や STATUS_ACCESS_VIOLATION
など)を発生させるようになります。
このコミットの背景には、おそらく HEAP_GENERATE_EXCEPTIONS
フラグの使用が、cmd/dist
の特定の状況下で予期せぬ挙動やクラッシュを引き起こしていた可能性があります。Goのランタイムやツールチェーンは、クロスプラットフォームでの安定性と予測可能性を重視するため、特定のOS固有の例外処理メカニズムに依存するのではなく、より一般的なエラーハンドリング(NULL
チェックなど)に切り替えることが望ましいと判断されたと考えられます。
前提知識の解説
Win32 APIとヒープメモリ管理
Windowsオペレーティングシステムでは、アプリケーションが動的にメモリを割り当てるためにWin32 APIが提供されています。その中でも、ヒープメモリ管理は重要な概念です。
- ヒープ (Heap): プログラムが実行時に動的にメモリを割り当てたり解放したりするための領域です。C/C++における
malloc
/free
やnew
/delete
に相当する機能を提供します。 GetProcessHeap()
: 現在のプロセスのデフォルトヒープへのハンドルを取得する関数です。ほとんどのアプリケーションは、このデフォルトヒープを使用します。HeapReAlloc()
: 指定されたヒープから割り当てられたメモリブロックのサイズを変更する関数です。- 第一引数: ヒープのハンドル。
- 第二引数: フラグ。メモリ再割り当ての動作を制御します。
- 第三引数: 再割り当てする既存のメモリブロックへのポインタ。
- 第四引数: 新しいメモリブロックのサイズ(バイト単位)。
- 成功した場合、再割り当てされたメモリブロックへのポインタを返します。失敗した場合、
NULL
を返します。
HEAP_GENERATE_EXCEPTIONS
フラグ:HeapAlloc
,HeapReAlloc
,HeapCreate
などのヒープ関数で使用できるフラグです。このフラグが設定されている場合、メモリ割り当てや再割り当てが失敗した際に、関数がNULL
を返す代わりに、構造化例外(Structured Exception Handling, SEH)を発生させます。STATUS_NO_MEMORY
: メモリ不足の場合に発生する例外。STATUS_ACCESS_VIOLATION
: ヒープ破損など、不正なメモリ操作があった場合に発生する例外。- このフラグを使用すると、エラー処理を例外ベースで行うことができますが、例外処理のオーバーヘッドや、予期せぬ例外がプログラムの安定性を損なう可能性もあります。
構造化例外処理 (SEH)
Windowsの構造化例外処理 (SEH) は、ハードウェア例外(ゼロ除算、アクセス違反など)やソフトウェア例外(RaiseException
で明示的に発生させる例外)を処理するためのメカニズムです。C/C++では __try
, __except
, __finally
ブロックを使用して例外を捕捉・処理します。
HEAP_GENERATE_EXCEPTIONS
フラグは、このSEHメカニズムを利用してメモリ割り当ての失敗を通知します。このフラグを使用しない場合、関数はエラーコード(この場合は NULL
)を返すため、呼び出し元は明示的に戻り値をチェックしてエラーを処理する必要があります。
技術的詳細
変更が行われたファイルは src/cmd/dist/windows.c
です。このファイルは、GoのディストリビューションツールがWindows上で動作する際の、OS固有の処理(特にメモリ管理)を実装しています。
問題の箇所は xrealloc
関数です。この関数は、Goのツールチェーン内で使用されるカスタムのメモリ再割り当て関数であり、内部的にWin32 APIの HeapReAlloc
を呼び出しています。
元のコードでは、HeapReAlloc
の呼び出しに HEAP_GENERATE_EXCEPTIONS
フラグが渡されていました。
p = HeapReAlloc(HEAP, HEAP_GENERATE_EXCEPTIONS, p, n);
このフラグが存在すると、HeapReAlloc
がメモリ再割り当てに失敗した場合、STATUS_NO_MEMORY
などの例外が発生します。しかし、Goのツールチェーンの設計思想や、より移植性の高いエラーハンドリングの観点から、この例外ベースの挙動は望ましくないと判断された可能性があります。
変更後のコードでは、HEAP_GENERATE_EXCEPTIONS
フラグが 0
に置き換えられています。
p = HeapReAlloc(HEAP, 0, p, n);
0
はフラグが何も設定されていないことを意味します。これにより、HeapReAlloc
が失敗した場合、例外を発生させる代わりに NULL
を返すようになります。その後のコードでは、p == nil
(Goの NULL
に相当)をチェックしてメモリ不足を検出しています。
if(p == nil)
fatal("out of memory reallocating %d", n);
この変更により、メモリ再割り当ての失敗は、Win32の構造化例外ではなく、Goのツールチェーン内で一般的な NULL
チェックと fatal
関数によるエラー報告という、より統一された方法で処理されるようになります。これは、コードの可読性、保守性、そしてクロスプラットフォーム互換性の向上に寄与します。
コアとなるコードの変更箇所
--- a/src/cmd/dist/windows.c
+++ b/src/cmd/dist/windows.c
@@ -735,7 +735,7 @@ xrealloc(void *p, int n)
return xmalloc(n);
if(HEAP == INVALID_HANDLE_VALUE)
HEAP = GetProcessHeap();
- p = HeapReAlloc(HEAP, HEAP_GENERATE_EXCEPTIONS, p, n);
+ p = HeapReAlloc(HEAP, 0, p, n);
if(p == nil)
fatal("out of memory reallocating %d", n);
return p;
コアとなるコードの解説
変更されたのは src/cmd/dist/windows.c
ファイル内の xrealloc
関数です。
xrealloc
関数は、Goのビルドツールが内部的に使用するメモリ再割り当てのラッパー関数です。
if(p == nil)
: 渡されたポインタp
がnil
(NULL
)の場合、これは新しいメモリブロックの割り当て要求と見なされ、xmalloc(n)
を呼び出して新しいメモリを割り当てます。if(HEAP == INVALID_HANDLE_VALUE)
:HEAP
変数がまだ初期化されていない場合(INVALID_HANDLE_VALUE
は無効なハンドルを示す定数)、GetProcessHeap()
を呼び出して現在のプロセスのデフォルトヒープのハンドルを取得し、HEAP
に格納します。これにより、以降のヒープ操作で同じヒープが使用されます。p = HeapReAlloc(HEAP, 0, p, n);
: ここが変更の核心です。- 以前は
HEAP_GENERATE_EXCEPTIONS
フラグが第二引数に渡されていましたが、これが0
に変更されました。 - これにより、
HeapReAlloc
がメモリ再割り当てに失敗した場合、例外を発生させる代わりにNULL
を返します。
- 以前は
if(p == nil)
:HeapReAlloc
の呼び出し後、返されたポインタp
がnil
であるかどうかをチェックします。- もし
nil
であれば、メモリ再割り当てが失敗したことを意味します。 - この場合、
fatal("out of memory reallocating %d", n);
を呼び出して、指定されたサイズのメモリ再割り当てに失敗したことを示す致命的なエラーメッセージを出力し、プログラムを終了します。
- もし
return p;
: 成功した場合、再割り当てされたメモリブロックへのポインタを返します。
この変更により、xrealloc
関数は、Windowsの構造化例外メカニズムに依存することなく、HeapReAlloc
の戻り値(NULL
)を直接チェックすることでメモリ不足エラーを処理するようになりました。これは、Goのツールチェーン全体でより一貫性のあるエラーハンドリングパターンに沿ったものです。
関連リンク
- Go Issue Tracker (CL 5650048): https://golang.org/cl/5650048 (コミットメッセージに記載されている変更リストへのリンク)
参考にした情報源リンク
- Microsoft Docs: HeapReAlloc function: https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heaprealloc
- Microsoft Docs: HeapAlloc function: https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc
- Microsoft Docs: HeapCreate function: https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapcreate
- Microsoft Docs: GetProcessHeap function: https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-getprocessheap
- Microsoft Docs: Structured Exception Handling (C/C++): https://learn.microsoft.com/en-us/windows/win32/debug/structured-exception-handling
- Stack Overflow: What does HEAP_GENERATE_EXCEPTIONS do?: https://stackoverflow.com/questions/1000000/what-does-heap-generate-exceptions-do