私は C++ のバックグラウンドを持っており、約 1 年間 C# を使用しています。他の多くの人と同じように、決定論的リソース管理が言語に組み込まれていない理由について、私は困惑しています。
このusing
コンストラクトは、"決定論的" リソース管理を提供し、C# 言語に組み込まれています。「決定論的」とは、ブロックの実行が開始Dispose
された後、コードの前に呼び出されることが保証されていることを意味することに注意してください。using
また、これは「決定論的」という言葉が意味するものではありませんが、この文脈では誰もがそれをそのように乱用しているように見えることに注意してください。
C++ に偏った私の脳では、決定論的デストラクタで参照カウント スマート ポインターを使用することは、メモリ以外のリソースをクリーンアップするために IDisposable を実装し、dispose を呼び出す必要があるガベージ コレクターからの大きな一歩のようです。
ガベージ コレクターを実装する必要はありませんIDisposable
。実際、GC はそれを完全に無視しています。
確かに、私はあまり頭がよくありません...なぜ物事がそのようになっているのかをよりよく理解したいという純粋な願望からこれを求めています.
ガベージ コレクションのトレースは、無限メモリ マシンをエミュレートする高速で信頼性の高い方法であり、手動のメモリ管理の負担からプログラマを解放します。これにより、いくつかのクラスのバグ (ダングリング ポインター、すぐに解放される、二重に解放される、解放するのを忘れる) が排除されました。
C# が次のように変更された場合:
オブジェクトは参照カウントされます。オブジェクトの参照カウントがゼロになると、リソースのクリーンアップ メソッドがオブジェクトに対して決定論的に呼び出されます。
2 つのスレッド間で共有されるオブジェクトを考えてみましょう。スレッドは、参照カウントを 0 に減らすために競合します。1 つのスレッドがレースに勝ち、もう 1 つのスレッドがクリーンアップを担当します。それは非決定論的です。参照カウントが本質的に決定論的であるという信念は神話です。
別の一般的な神話は、参照カウントがプログラムの可能な限り早い時点でオブジェクトを解放するというものです。そうではありません。デクリメントは常に、通常はスコープの最後まで延期されます。これにより、オブジェクトが必要以上に長く存続し、いわゆる「浮遊ガベージ」が放置されます。特に、一部のトレース ガベージ コレクタは、スコープベースの参照カウントの実装よりも早くオブジェクトをリサイクルできます。
次に、オブジェクトにガベージ コレクションのマークが付けられます。ガベージ コレクションは、将来の非決定的な時点で発生し、その時点でメモリが再利用されます。このシナリオでは、IDisposable を実装したり、忘れずに Dispose を呼び出したりする必要はありません。
IDisposable
いずれにせよ、ガベージ コレクションされたオブジェクトを実装する必要はないため、メリットはありません。
解放するメモリ以外のリソースがある場合は、リソースのクリーンアップ関数を実装するだけです。
なぜそれが悪い考えなのですか?
単純な参照カウントは非常に遅く、サイクルをリークします。たとえば、C++の Boostshared_ptr
は、OCaml のトレース GC よりも最大 10 倍遅くなります。単純なスコープベースの参照カウントでさえ、マルチスレッド プログラム (ほとんどすべての最近のプログラム) の存在下では非決定論的です。
それはガベージコレクターの目的を無効にしますか?
全然違います。実際、それは 1960 年代に発明され、その後 54 年間にわたって集中的な学術研究が行われ、参照カウントは一般的なケースでは最悪であると結論付けられた悪い考えです。
そのようなことを実装することは実現可能でしょうか?
絶対。初期のプロトタイプ .NET と JVM は参照カウントを使用していました。彼らはまた、GC のトレースを支持して、それがひどいものであることを発見し、それを落としました。
編集:これまでのコメントから、これは悪い考えです。
GC は参照カウントなしで高速です
はい。カウンターのインクリメントとデクリメントを延期することで、参照カウントを大幅に高速化できることに注意してください。しかし、それはあなたが切望する決定論を犠牲にし、今日のヒープサイズで GC をトレースするよりも遅いことに注意してください。ただし、参照カウントは漸近的に高速であるため、将来ヒープが非常に大きくなった時点で、本番環境の自動メモリ管理ソリューションで RC の使用を開始する可能性があります。
オブジェクトグラフのサイクルを扱う問題
トライアル削除は、参照カウント システムでサイクルを検出して収集するために特別に設計されたアルゴリズムです。ただし、それは遅く、非決定論的です。
1 番は有効だと思いますが、2 番は弱参照を使用すると簡単に対処できます。
弱参照を「簡単」と呼ぶことは、現実に対する希望の勝利です。彼らは悪夢です。それらは予測不能で設計が難しいだけでなく、API を汚染します。
速度の最適化は、次のような短所を上回ります。
タイムリーにメモリ以外のリソースを解放しない可能性があります
using
タイムリーにメモリ以外のリソースを解放しませんか?
リソースのクリーンアップ メカニズムが決定論的であり、言語に組み込まれている場合は、それらの可能性を排除できます。
このusing
構造は決定論的であり、言語に組み込まれています。
あなたが本当に聞きたい質問は、IDisposable
参照カウントを使用しない理由だと思います。私の回答は逸話的です: 私は 18 年間、ガベージ コレクション言語を使用してきましたが、参照カウントに頼る必要はありませんでした。したがって、弱い参照のような付随的な複雑さで汚染されていない、より単純な API を好みます。