手動メモリ管理

コンピュータサイエンスにおいて、手動メモリ管理とは、プログラマが手動の命令を使用して、使用されていないオブジェクト、つまりガベージを識別して解放することを指します。1990年代半ばまで、業界で使用されていたプログラミング言語の大部分は手動メモリ管理をサポートしていましたが、ガベージコレクションは1959年にLispで導入されて以来存在していました。[ 1 ]しかし、今日では、 Javaなどのガベージコレクションを備えた言語の人気が高まっており、 Objective-CSwiftなどの言語は、自動参照カウントを通じて同様の機能を提供しています。現在でも広く使用されている主な手動管理言語はCC++です。Cの動的メモリ割り当てを参照してください。

説明

多くのプログラミング言語では、自由記憶から新しいオブジェクトを割り当てるmallocタイミングを決定するために手動の手法を使用しています。C では関数を使用し、C++ と Java ではnew演算子を使用し、他の多くの言語 (Python など) ではすべてのオブジェクトを自由記憶から割り当てます。オブジェクトをいつ作成するべきか (オブジェクト作成) の決定は一般に簡単で問題はありませんが、オブジェクト プールなどの手法を使用すると、オブジェクトがすぐに使用される前に作成されることがあります。実際の課題はオブジェクトの破棄、つまりオブジェクトが不要になった (つまりガベージになった) タイミングの決定と、その基礎となるストレージを自由記憶に戻して再利用できるようにすることです。手動のメモリ割り当てでは、これもプログラマによって手動で指定されます。Cfree()の などの関数、またはC++ の 演算子を介してです。これは、C および C++ でスコープの終了時に破棄される、特に関数の (非静的)ローカル変数などの自動変数deleteに保持されるオブジェクトの自動破棄とは対照的です。

手動メモリ管理テクニック

例えば:

手動管理と正確性

手動メモリ管理は、不適切に使用されると、プログラムにいくつかの主要なバグ、特にメモリ安全性の侵害やメモリリークを引き起こすことが知られています。これらはセキュリティバグの大きな原因となります。[ 2 ]

  • 未使用のオブジェクトがフリーストアに解放されない場合、これはメモリリークと呼ばれます。場合によっては、メモリリークは許容されることもあります。例えば、プログラムの実行中に一定量のメモリを「リーク」するプログラムや、終了時にオペレーティングシステムがリソースを解放することに依存する短期実行プログラムなどです。しかし、多くの場合、メモリリークは長期実行プログラムで発生し、そのような場合には無制限の量のメモリがリークされます。このような場合、利用可能なフリーストアのサイズは時間の経過とともに減少し続け、最終的に使い果たされると、プログラムはクラッシュします。
  • 動的メモリ管理システムの壊滅的な障害は、オブジェクトのバッキング メモリが複数回削除された場合、オブジェクトが明示的に複数回破棄された場合、フリー ストアに割り当てられていないオブジェクトをポインターを使用して操作しているときに、プログラマーがそのポインターの対象オブジェクトのバッキング メモリを解放しようとした場合、または、未知の外部タスク、スレッド、またはプロセスによって管理されている別の任意のメモリ領域へのポインターを介してオブジェクトを操作しているときに、プログラマーがオブジェクトの状態を破壊し、境界外に書き込んでメモリ管理データを破壊した場合に発生することがあります。このようなアクションの結果として、ヒープ破損、メモリ内で複数回削除されたオブジェクトと同じ場所を占める別の(新しく作成された) オブジェクトの早期破壊、セグメンテーション違反(メモリ保護違反) によるプログラム クラッシュ、その他の未定義の動作が発生する可能性があります。
  • 削除されたオブジェクトへのポインターは、削除後に使用するとワイルド ポインターになります。このようなポインターを使用しようとすると、診断が困難なバグが発生する可能性があります。

ガベージコレクションのみを使用する言語は、最後の2つのクラスの欠陥を回避できることが知られています。メモリリークは依然として発生する可能性があります(世代別ガベージコレクションや保守的ガベージコレクションでは、境界付きリークが頻繁に発生します)が、一般的に手動システムにおけるメモリリークほど深刻ではありません。

リソースの取得は初期化です

手動メモリ管理には、リソース取得初期化(RAII) パラダイム を介して自動リソース管理が可能になるという正確性の利点が 1 つあります。

これは、オブジェクトが希少なシステムリソース(グラフィックリソース、ファイルハンドル、データベース接続など)を所有し、オブジェクトが破棄される際にそれらのリソースを放棄しなければならない場合に発生します。つまり、リソースの所有権の存続期間はオブジェクトの存続期間と結び付けられるべきです。手動管理機能を持つ言語では、オブジェクトの初期化時(コンストラクタ内)にリソースを取得し、オブジェクトの破棄時(デストラクタ内)に正確なタイミングで解放することで、この問題を解決できます。これは「リソース取得は初期化である」と呼ばれます。

これは決定論的参照カウントでも使用できます。C++では、この機能は、通常は手動で行うフレームワーク内でメモリ解放を自動化するためにさらに活用されています。shared_ptr言語の標準ライブラリのテンプレートを使用してメモリ管理を行うのは、一般的なパラダイムです。ただし、これはすべてのオブジェクト使用パターンに適している わけでshared_ptrありません。

このアプローチは、ほとんどのガベージコレクション言語、特にトレースガベージコレクタやより高度な参照カウントでは利用できません。これは、ファイナライズが非決定的であり、時には全く行われない可能性があるためです。つまり、ファイナライザメソッドがいつ呼び出されるか、あるいは呼び出されるかどうかを定義する(または決定する)ことが困難です。これは一般にファイナライザ問題として知られています。Javaやガベージコレクタを実装する他の言語では、メモリ以外の希少なシステムリソースを、 disposeパターンを介して手動で管理することが頻繁に用いられます。リソースを管理するオブジェクトはすべてdispose()、disposeパターンを実装することが期待され、このメソッドはリソースを解放し、オブジェクトを非アクティブとしてマークします。プログラマは、dispose()希少なグラフィックリソースの「リーク」を防ぐために、必要に応じて手動でこのメソッドを呼び出すことが期待されます。スタックリソース(単一のコードブロック内で取得および解放されるリソース)の場合、これはPythonのdispose with、C#のdispose、usingまたはJavaのtry-with-resourcesなどのさまざまな言語構成要素によって自動化できます。

パフォーマンス

手動メモリ管理の支持者の多くは、ガベージコレクションなどの自動手法と比較して、手動メモリ管理の方が優れたパフォーマンスを発揮すると主張しています。従来はレイテンシが最大のメリットでしたが、もはやそうではありません。手動割り当ては、参照の局所性において優れた点が多いのです。

手動割り当ては、メモリが希少なリソースであるシステムでは、より高速な回収が可能であるため、より適していることが知られています。メモリシステムは、プログラムのワーキングセットのサイズが利用可能なメモリのサイズに近づくと、頻繁に「スラッシング」を起こす可能性があり、実際に頻繁に発生します。ガベージコレクションシステムでは、未使用のオブジェクトはすぐに回収されないため、手動管理システムよりも回収されない状態が長く続きます。そのため、実効ワーキングセットサイズが増加します。

手動管理には、パフォーマンス上の欠点が数多くあることが文書化されています。

  • などの呼び出しは、delete実行されるたびにオーバーヘッドが発生しますが、このオーバーヘッドはガベージコレクションサイクルで償却できます。これは特に、削除呼び出しを同期させる必要があるマルチスレッドアプリケーションに当てはまります。
  • 割り当てルーチンはより複雑になり、処理速度も低下する可能性があります。ヒープ圧縮などの一部のガベージコレクション方式では、フリーストアを単純なメモリ配列として管理できます(手動管理方式で必要な複雑な実装とは対照的です)。

レイテンシは、時間の経過とともに変化してきた議論の的となっているポイントです。初期のガベージ コレクターと単純な実装では、手動のメモリ管理に比べてパフォーマンスが非常に劣っていましたが、洗練された最新のガベージ コレクターでは、手動のメモリ管理と同等かそれ以上のパフォーマンスを発揮することがよくあります。

手動割り当てでは、単純なストップザワールド ガベージ コレクションで発生する長い「一時停止」時間の影響を受けませんが、最近のガベージ コレクターでは、気づかれないほどのコレクション サイクルが行われます。

手動メモリ管理とガベージコレクションはどちらも、解放時間が無制限になる可能性があるという問題を抱えています。手動メモリ管理の場合、単一のオブジェクトの解放には、そのメンバーの解放、さらにはそのメンバーのさらにメンバーの解放などが必要になる可能性があり、ガベージコレクションではコレクションサイクルが長くなる可能性があります。これは特にリアルタイムシステムで問題となり、無制限のコレクションサイクルは一般的に許容されません。リアルタイムガベージコレクションはガベージコレクタを一時停止することで可能ですが、リアルタイムの手動メモリ管理では、大規模な解放を避けるか、手動で解放を一時停止する必要があります。

参照

参考文献

  1. ^ McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I. (1985) [1962]. LISP 1.5 プログラマーズ・マニュアル(PDF) . 第15刷 (第2版). p. 序文.
  2. ^ 「C++の開発者が「深刻な攻撃」に対処するための行動を呼びかける. 2026年1月14日時点のオリジナルよりアーカイブ2026年1月16日閲覧。