非決定論的なリソース管理は漏れやすい抽象化ですか?


9

私が見ることができることから、リソース管理には、決定論的破壊と明示的破壊という2つの一般的な形式があります。前者の例は、C ++デストラクタとスマートポインタまたはPerlのDESTROYサブです。後者の例は、Rubyのブロックから管理リソースへのパラダイムまたは.NETのIDisposeインターフェイスです。

新しい言語は後者を選択しているようです。おそらく、参照カウント以外の種類のガベージコレクションシステムを使用することの副作用としてです。

私の質問はこれです:スマートポインターまたは参照カウントガベージコレクションシステムのデストラクタ-ほとんど同じこと-が暗黙的かつ透過的なリソース破壊を可能にすることを考えると、それは明示に依存する非決定的タイプよりもリークの少ない抽象化です表記?

具体的な例を挙げましょう。単一のスーパークラスのC ++サブクラスが3つある場合、特定の破棄を必要としない実装がある可能性があります。多分それは別の方法でその魔法をします。特別な破棄を必要としないという事実は関係ありません。すべてのサブクラスが同じように使用されます。

別の例では、Rubyブロックを使用しています。2つのサブクラスはリソースを解放する必要があるため、スーパークラスはコンストラクターでブロックを使用するインターフェースを選択します。ただし、他の特定のサブクラスは特別な破棄を必要としないため、ブロックを必要としない場合もあります。

後者はリソース破壊の実装の詳細をリークしますが、前者はリークしませんか?

編集:たとえば、RubyとPerlの比較は、一方は確定的破壊を持ち、もう一方はそうではないため、より公平である可能性がありますが、両方ともガベージコレクションされます。


5
私は「はい」と言いたくなりますが、これについて他の人が言わなければならないことを聞きたいです。
Bart van Ingen Schenau 2013年

透明な資源破壊?通常のポインターの代わりにスマートポインターを使用する必要があるという事実は別として?これは、オブジェクトにアクセスするためのメカニズム(参照)が1つしかない(C ++では少なくとも4つまたは5つある)よりも透過的ではないと思います。
Giorgio

@ジョルジオ:「オブジェクトにアクセスする方法」はかなりあいまいです。読み取りまたは書き込みを意味しますか?Const / Volatile資格?ポインタは、実際には「オブジェクトにアクセスする方法」ではありません。ほとんどすべての式はオブジェクトになり、ポインタの逆参照は特別なことではありません。
MSalters 2013年

1
@ジョルジオ:そのOOPの意味では、C ++ポインターにメッセージを送信することはできません。あなたは、メッセージ送信するためのポインタを間接参照する必要が(*ptr).Message()同等かをptr->Message()。同様に、許可された式の無限のセットがあり((*ptr))->Messageます。しかし、それらはすべて沸騰するexpressionIdentifyingAnObject.Message()
MSalters 2013年

1
refcountingでは、円を回避するように注意する必要があります。そのため、抽象化も同様にリークします。
CodesInChaos 2013年

回答:


2

あなた自身の例がその質問に答えます。透過的な破壊は、明示的な破壊よりも明らかに漏洩が少ないです。漏れる可能性がありますが、漏れは少なくなります。

明示的な破棄は、すべての落とし穴があるCのmalloc / freeに似ています。たぶん、それがスコープベースに見えるようにするために、いくつかの構文上の砂糖を使っています。

明示的で はなく透過的破棄の利点の一部:-
同じ使用パターン
-リソースを解放することを忘れることはできません。
-詳細をクリーンアップしても、使用時にランドスケープを散らかすことはありません。


2

抽象化の失敗は、実際にはガベージコレクションが非決定的であるという事実ではなく、オブジェクトが参照を保持しているものに「興味がある」という考えであり、保持していないものには興味がない参照。理由を確認するために、特定のコントロールが描画される頻度のカウンターを維持するオブジェクトのシナリオを考えます。作成時に、コントロールの「ペイント」イベントをサブスクライブし、処分時にサブスクライブ解除します。クリックイベントは単にフィールドをインクリメントし、メソッドgetTotalClicks()はそのフィールドの値を返します。

カウンターオブジェクトが作成されると、それが監視するコントロール内にそれ自体への参照が格納されるようにする必要があります。コントロールは本当にカウンターオブジェクトを気にせず、カウンターオブジェクトとその参照が存在しなくなっても同じように満足しますが、参照が存在する限り、毎回そのオブジェクトのイベントハンドラーを呼び出します。それは自分自身をペイントします。このアクションはコントロールにはまったく役に立たないものですがgetTotalClicks()、オブジェクトを呼び出す誰にとっても役に立ちます。

たとえば、メソッドが新しい「paint-counter」オブジェクトを作成し、コントロールに対して何らかのアクションを実行し、コントロールが再描画された回数を観察してから、paint-counterオブジェクトを破棄した場合、オブジェクトはイベントにサブスクライブされたままになりますただし、オブジェクトとそのオブジェクトへのすべての参照が単純に消滅しても、だれも気にしません。ただし、コントロール自体が有効になるまで、オブジェクトはコレクションの対象にはなりません。メソッドがコントロールの有効期間内に何千回も呼び出されるものである場合(もっともらしいシナリオ)、メモリオーバーフローを引き起こす可能性がありますが、N呼び出しのコストはO(N ^ 2)またはO (N ^ 3)サブスクリプション処理が非常に効率的で、ほとんどの操作が実際にはペイントを含まない場合を除きます。

この特定のシナリオは、コントロールに強いオブジェクトではなくカウンターオブジェクトへの弱い参照を維持させることで処理できます。弱いサブスクリプションモデルは役立ちますが、一般的なケースでは機能しません。単一のコントロールから単一の種類のイベントを監視するオブジェクトが必要な代わりに、複数のコントロールを監視するイベントロガーオブジェクトが必要で、システムのイベント処理メカニズムが各コントロールに参照が必要であると想定します。別のイベントロガーオブジェクトに。その場合、コントロールをイベントロガーにリンクするオブジェクトは、両方が存在する限り、存続している必要があります。監視されているコントロールとイベントロガーは引き続き有用です。コントロールもイベントロガーもリンクイベントへの強い参照を保持していない場合、それがまだ「有用」であるにもかかわらず、イベントは存在しなくなります。どちらかが強いイベントを保持している場合、リンクしているオブジェクトの寿命は、他のオブジェクトが死んだとしても無駄に延長される可能性があります。

オブジェクトへの参照がユニバースのどこにも存在しない場合、そのオブジェクトは無用であると見なされ、存在から削除されます。ただし、オブジェクトへの参照が存在するという事実は、オブジェクトが「有用」であることを意味するものではありません。多くの場合、オブジェクトの実際の有用性は、GCの観点からは、それらとはまったく関係のない他のオブジェクトへ参照の存在に依存します。

オブジェクトに誰も興味がないときに決定論的に通知された場合、オブジェクトはその情報を使用して、その知識から恩恵を受けるだろう誰にでも通知されるようにすることができます。ただし、そのような通知がない場合、存在する参照のセットだけがわかっていて、それらの参照に関連付けられている意味的な意味がわからない場合、どのオブジェクトが「有用」と見なされるかを判断する一般的な方法はありません。したがって、GCがオブジェクトの破棄を即座に検出できたとしても、参照の存在または非存在が自動リソース管理に十分であると想定しているモデルは運命づけられません。


0

いいえ」デストラクタ、または「このクラスは破棄する必要がある」と言う他のインターフェイスは、そのインターフェイスのコントラクトです。特別な破棄を必要としないサブタイプを作成した場合、Liskov置換原則の違反と見なす傾向があります。

C ++と他のものとの違いについては、それほど大きな違いはありません。C ++は、すべてのオブジェクトにそのインターフェイスを強制します。抽象化は、言語で必要とされるときにリークできません。


4
「特別な破棄を必要としないサブタイプを作成する場合」これはLSP違反ではありません。no-opは破棄の有効な特別なケースであるためです。問題は、破壊要件を派生クラスに追加するときです。
CodesInChaos 2013年

私はここで混乱しています。C ++サブクラスに特別な破棄コードを追加する必要がある場合、自動であるため、使用パターンはまったく変更されません。つまり、スーパークラスとサブクラスは互換的に使用できます。しかし、リソース管理の明示的な表記を使用すると、明示的な破棄を必要とするサブクラスは、その使用法をスーパークラスと互換性がなくなるでしょう。(スーパークラスが明示的な破棄を必要としないと仮定します。)
Louis Jackman

@CodesInChaos-ああ、そうだと思う。
Telastyn

@ljackman:特別な破棄を必要とするクラスは、コンストラクターを呼び出して実行されることを保証するために、誰にでも負担をかけます。は、DerivedFooThatRequiresSpecialDestructionを呼び出すコードによってのみ作成できるため、LSP違反は発生しませんnew DerivedFooThatRequiresSpecialDestruction()。一方、DerivedFooThatRequiresSpecialDestruction破棄が必要なものを予期していないコードにを返したファクトリメソッドは、LSP違反になります。
スーパーキャット2014

0

私の質問はこれです:スマートポインターまたは参照カウントガベージコレクションシステムのデストラクタ-ほとんど同じこと-が暗黙的かつ透過的なリソース破壊を可能にすることを考えると、それは明示に依存する非決定的タイプよりもリークの少ない抽象化です表記?

手でサイクルを監視する必要があることは、暗黙的でも透過的でもありません。唯一の例外は、設計によりサイクルを禁止する言語を使用した参照カウントシステムです。Erlangはそのようなシステムの例かもしれません。

したがって、両方のアプローチがリークします。主な違いは、C ++ではデストラクタがどこでもリークするがIDispose、.NETでは非常にまれであることです。


1
サイクルが非常にまれであることを除いて、明示的に循環するデータ構造を除いて、実際には決して発生しません。主な違いは、C ++のデストラクタはどこでも適切に処理されますが、IDisposeが.NETの問題を処理することはめったにありません。
DeadMG

「サイクルが非常にまれであることを除いて」。現代の言語では?私はその仮説に挑戦します。
Jon Harrop、2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.