テクノロジー · 実用ガイド

mmap が exported pointers で閉じられない?配列を変える前に全ての借用側を追う

親ビューを解放し配列名を削除しても、スライスや別名が残る場合があります。結果がマッピングを借用するのか、独立したコピーを持つのか確認します。

借用とコピー8ケースのマッピング終了結果を示すイラスト
独自の実測イラスト。6つの生存する借用は終了を妨げ、2つの独立コピーは終了後も読めました。対象は本検証の CPython3.12.14/NumPy2.3.5 のみです。 Watch at Ease

mmap.close() の BufferError は、借用側がまだメモリーを必要としている兆候かもしれません。配列の構築方法を変えて例外を消すだけでは、安全な寿命を証明できません。

マッピング、直接ビュー、派生ビュー、保持される全配列参照の関係図から始めます。ローカル8ケースで、名前の削除、借用の終了、独立データの作成を区別します。

1. 所有者と外へ渡る借用側を特定

Python と NumPy のバージョン、マッピングのコンストラクター、配列の dtype、各結果が外へ渡る場所を記録します。np.frombuffer は入力へのビューを作ります。基本スライスは別のビューになり得て、alias = arr は同じ配列に別の名前を付けるだけです。どちらも独立データバッファーを保証しません。

後処理の前に API の契約を決めます。借用結果を使うなら全利用側が終わるまで所有者を生存させます。所有者より長く残す結果には独立ストレージが必要です。読み取り専用とは、そのビュー経由の書き込み許可の話です。借用メモリーが所有済みコピーになるわけではありません。

2. ローカル8ケースの結果

Linux の CPython 3.12.14 と NumPy 2.3.5 を使い、各ケースで値 0 から 7 を持つ8バイトの匿名マッピングを作りました。直接の memoryview は release() まで終了を妨げました。親を解放してもスライスが残ると終了できず、スライスの解放後に閉じられました。派生した読み取り専用 memoryview でも同様でした。

np.frombuffer(mm,dtype=np.uint8) の配列が生きている間は終了できませんでした。arr を削除しても arr[:2] を残すと終了を妨げ、alias = arr を残した場合も同じでした。この単純な CPython 入力では、残る NumPy 借用参照を除くと閉じられました。キャッシュ、コンテナー、クロージャー、循環参照を含むコードで del arr が一般的な終了保証になるわけではありません。

3. コピーは寿命の契約を変える

7番目のケースは owned = arr.copy() で独立した uint8 配列を作りました。借用配列を削除するとマッピングを閉じられ、owned.tolist() はその後も 0 から 7 を返しました。8番目は owned = mm[:2] で独立 bytes を作り、終了後も 0 と 1 を保持しました。追加のメモリー割り当てを伴うコピーであり、マッピングとの継続同期はありません。

ソースが有効な間に、外へ渡す必要のある範囲だけコピーします。ローカル名の削除では別の呼び出し側の別名を無効化できません。NumPy の内部 base 参照鎖を手動解放したり、終了エラーを抑制したり、ガードを避けるためだけにコンストラクターを変えたりしないでください。ゼロコピーが必要なら明示的なアプリ契約で所有者の寿命を延ばします。

4. 終了と残すデータを別々に検証

memoryview では、保持した全子ビューと親を意図したスコープで終了します。コンテキストマネージャーは実際に管理するビューにしか効きません。NumPy では所有者を閉じる前に全借用スライスと別名を終了します。失敗時はオブジェクトの参照関係を調べ、close を繰り返したり、ガベージコレクションを借用不存在の証明と見なしたりしないでください。

8ケース全てで、それぞれの後処理後に終了成功を確認しました。終了後に読んだのは独立コピーだけで、アンマップ後の借用ポインターは参照していません。実ファイル、共有メモリーの unlink、同時利用側、他の OS は未検証です。実アプリでは終了経路とコピーの値を別々に確認します。ファイルのフラッシュと永続化は別の契約であり、残るエクスポート済みビューの解決にはなりません。

マッピングの所有者、借用参照、独立コピーの寿命のイラスト
独自の所有権イラスト。ゼロコピー結果には有効な所有者が必要で、独立コピーは終了境界を越えられます。終了成功とデータ所有権を別々に確認します。 Watch at Ease