ガベージ コレクションは、LISP の初期の頃から存在しており、数十年経った現在、ほとんどの最新のプログラミング言語で使用されています。
これらの言語のいずれかを使用していると仮定すると、ガベージ コレクションを使用せず、代わりに何らかの方法でメモリ割り当てを手動で管理する必要がある理由は何ですか?
これをしなければならなかったことがありますか?
できれば具体例を教えてください。
ガベージ コレクションは、LISP の初期の頃から存在しており、数十年経った現在、ほとんどの最新のプログラミング言語で使用されています。
これらの言語のいずれかを使用していると仮定すると、ガベージ コレクションを使用せず、代わりに何らかの方法でメモリ割り当てを手動で管理する必要がある理由は何ですか?
これをしなければならなかったことがありますか?
できれば具体例を教えてください。
私はいくつか考えることができます:
確定的な割り当て解除/クリーンアップ
リアルタイムシステム
アルゴリズムに応じて、メモリまたはプロセッサ時間の半分をあきらめない
メモリの割り当て/割り当て解除と、アプリケーション固有の割り当て、割り当て解除、およびメモリの管理の高速化。基本的に、独自のメモリを作成します-通常はパフォーマンスに敏感なアプリ用です。これは、アプリケーションの動作が十分に理解されている場合に実行できます。汎用 GC (Java や C# など) では、これは不可能です。
編集
そうは言っても、GC は確かにコミュニティの多くにとって良いものでした。これにより、気の利いたプログラミングのトリックやパターンではなく、問題の領域に集中することができます。ただし、私はまだ「管理されていない」C++ 開発者です。その場合、グッドプラクティスとツールが役立ちます。
メモリ割り当て?いいえ、GC の方が私よりも優れていると思います。
しかし、ファイル ハンドル、データベース接続など、リソースの割り当てが不足していませんか? 完了したらそれらを閉じるコードを書きます。GC はそれを行いません。
私は多くの組み込み開発を行っていますが、問題は malloc を使用するか静的割り当てを使用するかである可能性が高く、ガベージ コレクションはオプションではありません。
私はまた、多くの PC ベースのサポート ツールを作成しており、GC が利用可能で十分に高速な場合は喜んで GC を使用します。つまり、pedant::std::string を使用する必要はありません。
私は圧縮と暗号化のコードをたくさん書いていますが、GC のパフォーマンスは通常、実装を本当に曲げない限り十分ではありません。GC では、アドレス エイリアシングのトリックにも細心の注意を払う必要があります。私は通常、パフォーマンスに敏感なコードを C で記述し、Python / C# フロント エンドから呼び出します。
したがって、私の答えは、GC を避ける理由はあるということですが、その理由はほとんどの場合パフォーマンスであり、GC をだまそうとするよりも、GC を必要とするものを別の言語でコーディングするのが最善です。
MSVC++ で何かを開発する場合、ガベージ コレクションは使用しません。非標準であることも理由の 1 つですが、私が C++ で GC を使用せずに育ち、安全なメモリ再利用を自動的に設計したことも理由の 1 つです。そうは言っても、C++ は、C の翻訳の透過性と予測可能性、または後の OO 言語の (とりわけ) 範囲指定されたメモリの安全性を提供できない忌まわしいものだと思います。
ガベージ コレクタを使用してリアルタイム アプリケーションを作成するのは、おそらく難しいでしょう。別のスレッドで機能するインクリメンタル GC を使用する場合もありますが、これは追加のオーバーヘッドです。
私が考えることができる 1 つのケースは、数百メガバイト以上の大規模なデータ セットを扱っている場合です。状況によっては、このメモリを使い終わったらすぐに解放して、他のアプリケーションが使用できるようにすることもできます。
また、一部のアンマネージ コードを処理する場合、GC がアンマネージ部分によってまだ使用されているため、GC が一部のデータを収集しないようにする必要がある場合があります。ただし、参照を保持するだけでは不十分な理由を考える必要があります。:P
私が扱った 1 つの状況は、画像処理です。画像をトリミングするためのアルゴリズムに取り組んでいるときに、マネージド ライブラリは、大きな画像や一度に複数の画像を切り取るのに十分な速度ではないことがわかりました。
適切な速度で画像を処理する唯一の方法は、私の状況では非管理コードを使用することでした。これは、C# .NET で小さな個人的なサイド プロジェクトに取り組んでいたときでした。プロジェクトのサイズと、自分自身を向上させるためにサード パーティのライブラリを学びたかったため、サード パーティのライブラリについて学びたくありませんでした。それを実行できる既存のサードパーティ ライブラリ (おそらく Paint.NET) が存在する可能性がありますが、それでもアンマネージ コードが必要になります。
一言:宇宙硬化
私はそれが極端なケースであることを知っていますが、それでも適用可能です。火星探査機のコアに適用されたコーディング標準の 1 つは、動的メモリ割り当てを実際に禁止しています。これは確かに極端ではありますが、「心配なく展開して忘れる」という理想を示しています。
要するに、あなたのコードが誰かのコンピューターに対して実際に何をしているのかについて、ある程度の感覚を持ってください。もしそうなら、そしてあなたは保守的です..その後、記憶の妖精に残りの面倒を見てもらいましょう。クアッド コアで開発している間、ユーザーははるかに古いものを使用している可能性があり、メモリに余裕がありません。
ガベージ コレクションをセーフティ ネットとして使用し、何を割り当てるかに注意してください。
これらの言語のいずれかを使用していると仮定すると、ガベージ コレクションを使用せず、代わりに何らかの方法でメモリ割り当てを手動で管理する必要がある理由は何ですか?
いくつかの理由が考えられます。
ガベージ コレクターによるプログラムの待ち時間が許容できないほど長くなります。
リサイクルまでの遅延は容認できないほど長くなります。たとえば、.NET で大きな配列を割り当てると、まれに収集されるラージ オブジェクト ヒープ (LOH) に配置されるため、到達不能になった後もしばらくの間ハングアップします。
書き込みバリアなど、ガベージ コレクションに関連するその他のオーバーヘッドは許容できないほど高くなります。
ガベージ コレクターの特性は容認できません。たとえば、.NET で配列を 2 倍にすると、ラージ オブジェクト ヒープ (LOH) がフラグメント化され、理論的には十分な空き領域があるにもかかわらず、32 ビット アドレス空間が使い果たされるとメモリ不足が発生します。OCaml (およびおそらくほとんどの GC 化された言語) では、深いスレッド スタックを持つ関数は漸近的に遅くなります。また、OCaml では、GC のグローバル ロックによってスレッドの並列実行が妨げられているため、(理論的には) C に落として手動メモリ管理を使用することで並列処理を実現できます。
これをしなければならなかったことがありますか?
いいえ、そうする必要はありませんでした。私は楽しみのためにそれをしました。たとえば、ガベージ コレクターを F# (.NET 言語) で作成し、タイミングを代表的なものにするために、割り当てのないスタイルを採用して GC の待機時間を回避しました。実稼働コードでは、ガベージ コレクターがどのように機能するかの知識を使用してプログラムを最適化する必要がありましたが、.NET 内からそれを回避する必要さえありませんでした。
GC が並列処理を妨げているため、私がガベージ コレクションの廃止に最も近づいたのは、OCaml 言語自体の廃止でした。しかし、最終的に .NET 言語である F# に移行することになり、その結果、CLR の優れたマルチコア対応 GC を継承しています。
リアルタイム システムには、ハードとソフトの 2 つの主要なタイプがあります。主な違いは、ハード リアルタイム システムでは、アルゴリズムが常に特定のタイム バジェット内で終了することを要求することですが、ソフト システムでは通常はそれが行われることを望んでいます。ソフト システムは、適切に設計されたガベージ コレクタを使用できる可能性がありますが、通常のガベージ コレクタは受け入れられません。ただし、ハード リアルタイム システムのアルゴリズムが時間内に完了しない場合、命が危険にさらされる可能性があります。この種のシステムは、原子炉、飛行機、スペース シャトルに見られますが、オペレーティング システムとドライバーを構成する専門的なソフトウェアにしか見られません。これは、一般的なプログラミングの仕事ではないと言えば十分です。
これらのシステムを作成する人は、汎用プログラミング言語を使用する傾向がありません。Ada は、この種のリアルタイム システムを作成する目的で設計されました。一部のシステムではそのようなシステムの特別な言語であるにもかかわらず、この言語はさらに Spark と呼ばれるサブセットに縮小されています。Spark は、Ada 言語の特別なセーフティ クリティカルなサブセットであり、許可されていない機能の 1 つは、新しいオブジェクトの作成です。オブジェクトの new キーワードは、メモリ不足の可能性と可変実行時間のために完全に禁止されています。実際、Spark でのすべてのメモリ アクセスは絶対メモリ位置またはスタック変数で行われ、ヒープへの新しい割り当ては行われません。ガベージ コレクターはまったく役に立たないだけでなく、保証された実行時間に有害です。
この種のシステムは一般的ではありませんが、それらが存在する場合は、非常に特別なプログラミング手法が必要であり、実行時間の保証が重要です。
これらの答えはすべて、パフォーマンスとコントロールに帰着します。以前の投稿で見たことのない角度の 1 つは、GC をスキップすると、アプリケーションのキャッシュ動作が 2 つの点で予測しやすくなるということです。
ビデオゲームでは、ゲームフレームの間にガベージコレクターを実行したくありません。
たとえば、ビッグバッドが目の前にあり、ライフは10になります。あなたはクワッドダメージパワーアップに向かって走ることに決めました。パワーアップを手に入れるとすぐに、最強の武器で発砲するために敵の方を向く準備をします。
パワーアップが消えたとき、ゲームの世界がパワーアップのデータを削除しなければならないという理由だけでガベージコレクターを実行するのは悪い考えです。
ビデオゲームは通常、特定のマップに何が必要かを把握することでオブジェクトを管理します(これが、多数のオブジェクトを含むマップをロードするのに時間がかかる理由です)。一部のゲームエンジンは、特定のイベントの後にガベージコレクターを呼び出します(保存後、エンジンが近くに脅威がないことを検出したときなど)。
ビデオゲーム以外に、ガベージコレクションをオフにする正当な理由はありません。
編集:他のコメントを読んだ後、組み込みシステムとスペース硬化(それぞれビルとティンカーティムのコメント)もガベージコレクターをオフにする良い理由であることに気づきました
質問がよくわかりません。GC を使用する言語について質問されているので、次のような例を尋ねていると思います。
#1 を実行する理由を見つけたことはありませんが、#2 は時折発生する理由です。多くのガベージ コレクターは、ファイナライズのメカニズムを提供します。これは、オブジェクトにバインドするアクションであり、システムはオブジェクトが回収される前にそのアクションを実行します。しかし、多くの場合、システムはファイナライザーが実際に実行されるかどうかについて保証しないため、ファイナライズの有用性は限られます。
ガーベッジ・コレクトされた言語で私が主に行っていることは、私が行っている他の作業の単位当たりの割り当て数を注意深く監視することです。通常、割り当てはパフォーマンスのボトルネックであり、特に Java または .NET システムでは顕著です。ML、Haskell、または LISP などの言語では、通常、プログラムが狂ったように割り当てられるという考えで設計されているため、それほど問題になりません。
EDIT : コメントへの応答が長くなります。
パフォーマンスに関して言えば、アロケーターと GC をチームと見なす必要があることを、誰もが理解しているわけではありません。最先端のシステムでは、割り当ては連続した空き領域 (「ナーサリ」) から行われ、テストとインクリメントと同じくらい迅速です。しかし、割り当てられたオブジェクトの有効期間が信じられないほど短い場合を除き、オブジェクトは最終的に負債を負うことになります。つまり、ナーサリからコピーする必要があり、しばらく有効な場合は、何世代にもわたってコピーされる可能性があります。最適なシステムは、割り当てに連続した空き領域を使用し、ある時点でコピーから古いオブジェクトのマーク/スイープまたはマーク/スキャン/コンパクトに切り替えます。したがって、非常にうるさい場合は、次の場合に割り当てを無視しても問題ありません。
それ以外の場合、割り当てられたオブジェクトは最初は安価かもしれませんが、後で実行する必要がある作業を表しています。割り当て自体のコストがテストと増分である場合でも、割り当てを減らすことがパフォーマンスを向上させる最善の方法です。私は最先端のアロケーターとコレクターを使用して数十の ML プログラムを調整しましたが、これは今でも当てはまります。最高のテクノロジを使用しても、メモリ管理は一般的なパフォーマンスのボトルネックです。
そして、寿命が非常に短いオブジェクトでさえ、多くのアロケーターがうまく処理できないことに驚かれることでしょう。Lua 5.1.4 (世代別 GC を備えたおそらく最速のスクリプト言語) から、30 個の置換のシーケンスを置き換えることで大幅なスピードアップを得ました。それぞれが大きな式の新しいコピーを割り当て、次の同時置換を行いました。 30 の代わりに大きな式の 1 つのコピーを割り当てた 30 の名前。パフォーマンスの問題はなくなりました。
実行が重要であるほど、ガベージ コレクションを延期したくなりますが、ガベージ コレクションを延期する時間が長ければ長いほど、最終的には問題が大きくなります。
コンテキストを使用して必要性を判断します。
1.
2.
3.
4.
ガベージ コレクションを使用するシステムでは、単純なキャッシュ メカニズムを実装するためにウィーク ポインターが使用されることがあります。これは、強い参照を持たないオブジェクトは、メモリ プレッシャがガベージ コレクションをトリガーする場合にのみ割り当てが解除されるためです。ただし、ARC では、最後の強参照が削除されるとすぐに値の割り当てが解除されるため、弱参照はそのような目的には適していません。
参考文献