CにGCを実装することについての投稿を見ましたが、一部の人は、Cが弱く型付けされているため、それを実行できないと言っていました。 C++でGCを実装する方法を知りたい。
私はそれを行う方法についての一般的なアイデアが欲しいです。どうもありがとうございました!
これは、友人が私に言ったブルームバーグのインタビューの質問です。彼はその時にひどくやった。これに関するあなたのアイデアを知りたいです。
CおよびC++のガベージコレクションは、いくつかの理由で両方とも難しいトピックです。
ポインターは整数に型キャストでき、その逆も可能です。これは、整数を取得し、それをポインタに型キャストし、それを逆参照することによってのみ到達可能なメモリのブロックを持つことができることを意味します。ガベージコレクターは、実際にブロックに到達できる場合でもブロックに到達できないと思わないように注意する必要があります。
ポインターは不透明ではありません。ストップアンドコピーコレクターのような多くのガベージコレクターは、メモリブロックを移動したり、スペースを節約するためにそれらを圧縮したりします。 CおよびC++でポインター値を明示的に確認できるため、これを正しく実装するのは困難です。誰かが整数への型キャストでトリッキーなことをしている場合、メモリのブロックを移動した場合、整数を正しく更新したことを確認する必要があります。
メモリ管理は明示的に実行できます。ガベージコレクターは、ユーザーがいつでもメモリのブロックを明示的に解放できることを考慮する必要があります。
C++では、割り当て/割り当て解除とオブジェクトの構築/破棄が分離されています。オブジェクトを実際に構築せずにオブジェクトを保持するのに十分なスペースをメモリブロックに割り当てることができます。優れたガベージコレクタは、メモリを再利用するときに、そこに割り当てられる可能性のあるオブジェクトのデストラクタを呼び出すかどうかを知る必要があります。これは、標準ライブラリコンテナの場合に特に当てはまります。標準ライブラリコンテナでは、_std::allocator
_を使用して、効率上の理由からこのトリックを使用することがよくあります。
メモリはさまざまな領域から割り当てることができます。 CおよびC++は、組み込みのフリーストア(malloc/freeまたはnew/delete)から、またはmmap
または他のシステムコールを介してOSから、またC++の場合は_get_temporary_buffer
_または_return_temporary_buffer
_。プログラムは、サードパーティのライブラリからメモリを取得することもあります。優れたガベージコレクターは、これらの他のプール内のメモリへの参照を追跡できる必要があり、(おそらく)クリーンアップを担当する必要があります。
ポインターは、オブジェクトまたは配列の中央を指すことができます。 Javaのようなガベージコレクションされた多くの言語では、オブジェクト参照は常にオブジェクトの先頭を指します。 CおよびC++では、ポインターは配列の中央を指し、C++ではオブジェクトの中央を指します(多重継承が使用されている場合)。これにより、まだ到達可能なものを検出するロジックが大幅に複雑になる可能性があります。
つまり、要するに、CまたはC++のガベージコレクターを構築することは非常に困難です。 CおよびC++でガベージコレクションを行うほとんどのライブラリは、アプローチが非常に保守的であり、技術的に不健全です。たとえば、ポインターを取得して整数にキャストし、ディスクに書き込んでからロードしないと想定しています。後でまた戻ってきます。また、ポインターのサイズであるメモリ内の値はおそらくポインターである可能性があると想定しているため、ポインターが存在する可能性がゼロではないため、到達不能なメモリーの解放を拒否する場合があります。
他の人が指摘したように、 Boehm GC はCおよびC++のガベージコレクションを行いますが、前述の制限に従います。
興味深いことに、C++ 11には、プログラマが将来のガベージコレクションの取り組みを見越して、メモリ領域を到達可能および到達不能としてマークできる新しいライブラリ関数がいくつか含まれています。将来的には、この種の情報を使用して非常に優れたC++ 11ガベージコレクタを構築できる可能性があります。ただし、当面は、上記のルールを破らないように十分に注意する必要があります。
CはC++ではありませんが、どちらにも同じ「弱く型付けされた」問題があります。ただし、問題を引き起こすのは暗黙のタイプキャストではなく、特にデータ構造ライブラリで「punning」(タイプシステムを破壊)する傾向です。
are Cおよび/またはC++用のガベージコレクターがあります。 Boehmの保守的なコレクターはおそらく最もよく知られています。保守的であり、あるオブジェクトへのポインタのように見えるビットパターンを検出した場合、そのオブジェクトを収集しません。その値は完全に他のタイプの値である可能性があるため、オブジェクトを収集できますが、「保守的」は安全にプレイすることを意味します。
ただし、計算されたポインターを使用すると、保守的なコレクターでもだまされる可能性があります。たとえば、すべてのリストノードに次のノードと前のノードのアドレスの違いを示すフィールドがあるデータ構造があります。アイデアは、より複雑なイテレータを犠牲にして、ノードごとに1つのリンクを持つ二重リンクリストの動作を提供することです。ほとんどのノードへの明示的なポインタはどこにも存在しないため、誤って収集される可能性があります。
もちろん、これは非常に例外的な特別なケースです。
さらに重要なことは、両方ではなく、信頼できるデストラクタまたはガベージコレクションを使用できることです。ガベージサイクルが収集されると、コレクタは最初に呼び出すデストラクタを決定できません。
RAIIパターンはC++で広く普及しており、デストラクタに依存しているため、IMOの競合があります。有効な例外はあるかもしれませんが、私の意見では、ガベージコレクションが必要な場合は、ガベージコレクション用にゼロから設計された言語(Java、C#など)を使用する必要があります。
Boehm Garbage Collector を調べてください。
スマートポインタを使用するか、参照を追跡し、メモリ割り当てなどを処理する独自のコンテナオブジェクトを作成できます。スマートポインタがおそらく望ましいでしょう。多くの場合、動的なヒープ割り当てを完全に回避できます。
例えば:
char* pCharArray = new char[128];
// do some stuff with characters
delete [] pCharArray;
上記の場合、新規と削除の間に何かがスローされると、削除は実行されません。上記のようなものは、より安全に簡単に置き換えることができます"garbage collected" code:
std::vector<char> charArray;
// do some stuff with characters
ブルームバーグは、実用的なコーディングの観点から、不適切なインタビューの質問で有名です。ほとんどのインタビュアーと同様に、彼らは主に実際のソリューションよりもあなたの考え方とコミュニケーションスキルに関心があります。
あなたが見た主張は間違っています。 Boehm collector はCおよびC++をサポートします。 CまたはC++でガベージコレクタを記述する方法の概要については、Boehmコレクタのドキュメント(特に このページ )を読むことをお勧めします。
shared_ptr 構造体について読むことができます。
シンプルな reference-counting ガベージコレクターを実装します。
実際のガベージコレクタが必要な場合は、new演算子をオーバーロードできます。
Shared_ptrに似た構造体を作成し、Objectと呼びます。
これにより、作成された新しいオブジェクトがラップされます。演算子をオーバーロードすることで、GCを制御できます。
ここで必要なのは、多くの GCアルゴリズム のうちの1つを実装することだけです