2

マネージコードの主な利点の1つは、組み込みのメモリ管理です。ポインタ、バッファサイズ、終了したメモリの解放などを追跡する必要はありません。管理された側面がそれを行います。

では、なぜIDisposableインターフェースがあるのでしょうか。 MSDNによると、インターフェイスはウィンドウハンドルやファイルなどの管理されていないリソースを処理するためのものです。しかし、なぜDisposeメソッドを明示的に呼び出す(または使用するUsing)必要があるのでしょうか。

  1. オブジェクトがスコープ外に出たときにCLRが追跡できず、Dispose自動的に呼び出すことができないのはなぜですか?
Public Function DoSomething() As String
    Dim reader As New StreamReader("myfile.txt")
    Dim txtFromFile As String = reader.ReadToEnd()

    Return txtFromFile '<==== reader goes out of scope after this line, so call Dispose automatically
End Function
  1. 少なくとも、ガベージコレクターが最終的にそれに到達して電話をかけないのはなぜDisposeですか?

私は何が欠けていますか?

編集

何人かの人々(ここと提案する他の回答)は、GCが最終的Usingガベージコレクションに取り掛かるだけなので、ガベージコレクションは十分ではないと示唆しています。その引数が.NETの他のオブジェクトと区別された理由がわかりません。また、オブジェクトがより多くのリソースを消費すると言う前に、次のことを考慮してください。IDisposableIDisposableIDisposable

  1. 上記のMSDNIDisposableは、リソース要件に関係なく、管理されていないオブジェクト用であると述べています
  2. 非常にリソースを大量に消費する.NETオブジェクトを見てきました(System.Web.UI.PageまたはSystem.Data.Objects.ObjectContextはどうですか)。
4

5 に答える 5

2

コメントで述べたように、CLR はオブジェクトがいつスコープ外になるかを追跡しません。

あなたの例を見てみましょう:

Public Function DoSomething() As String
    Dim reader As New StreamReader("myfile.txt")
    Dim txtFromFile As String = reader.ReadToEnd()

    Return txtFromFile '<==== reader goes out of scope after this line, so call Dispose automatically
End Function

実際に必要なのは、メソッドの本体全体を分析し、このリーダーへの参照を別のメソッドに渡したかどうか、またはフィールドなどに参照を格納したかどうかを確認することです。

次に、他のメソッドが参照をどこかに保存したか、他のメソッドを呼び出したかなどを判断する必要があります。

次に、参照が他の場所に格納されている場合、その意図が後でその参照を使用し、そこに未処理のインスタンスを見つけることを期待するかどうかを推測する必要があります。

それをあなたの知識と比較してください。参照を別の場所に保存したか、この使い捨ての「所有権を取得した」他の何かに参照を渡したかどうかは、(うまくいけば)わかります。これらのどちらも発生していないことがわかっている場合は、Usingブロックを追加して、この知識をコンパイラに伝えることができます。


または、いずれにせよ、ガベージ コレクターは最終的にそれに到達して呼び出すことはありDisposeませんか?

a) オブジェクトがアンマネージ リソースを直接「含んでいる」場合、および b) このオブジェクトを実装した人がベスト プラクティスに従っている場合、アンマネージ リソースでクリーンアップを実行するファイナライザをこのオブジェクトに実装している必要があります (通常は、ファイナライザーとDispose)。

ただし、次のガベージ コレクションがいつ発生するはわかりません。それまでの間、同じ管理されていないリソースへのアクセスを他のプログラム、または自分のプログラムの別の部分に拒否している可能性があります。管理されていないリソースは不足しているものとして扱う必要があります。誰かが を実装している場合、そのリソースへのアクセスが不要になったことがわかったら、Dispose(明示的にまたは を介し​​て) それを呼び出してほしいと思っています。Using

于 2012-11-21T09:59:34.530 に答える
1

CLR は、オブジェクトがいつスコープ外になるかを追跡しません。ガベージ コレクターのポイントは、最終的には死んだオブジェクトを収集することです。しかし、キーワードは「やがて」です。いつ発生するかは保証されていないため、特定の時点で発生する必要がある場合は、それを発生させるために何かを行う必要があります。これは何Dispose()のためのものusingです。

一般に、CLRは、オブジェクトがスコープ外になるタイミングを追跡できません。これは、そのオブジェクトが作成されたスコープに存在しなくなる可能性がある一方で、参照が別のスコープ (おそらく別のスレッド) の別の関数に渡されている可能性があるためです。

オブジェクトを作成すると、それへの参照を取得するだけです。他のような参照。オブジェクトは現在のスコープに存在しません。オブジェクトが作成されたサイトへの特別な添付ファイルはありません。

これは C++ の状況とは異なります。C++ では、オブジェクトが作成されるとそこに存在し、その特定のスコープを離れるとデストラクタが呼び出されます(オブジェクトへの参照が別の場所に存在する場合は運が悪い)。

したがって、これは実際にはもう少し注意してガベージ コレクション環境でもう少しコードを書く必要がある状況の 1 つです。リソースの有効期間が、宣言されているスコープによって制限されていることに依存することはできないため、いつ破棄する必要があるかを明示的に指定する必要があります。

于 2012-11-21T10:06:26.703 に答える
1

他の回答からのすべての驚くべき有用な情報を 1 か所にまとめるためだけに、.NETIDisposable.

リソースの可用性

  1. ガベージ コレクション スキーム (つまり、「マネージ コード」) は、メモリ管理用に設計されており、非常に優れています。
  2. ファイルハンドル、データベース接続、ソケット、ウィンドウ、プロセスなど、他のタイプのシステムリソースがあります。
  3. GC の重要な機能の 1 つは、できるだけ早くメモリをクリーンアップするのではなく、他の何かがメモリを必要とするまで、放棄されたメモリをそのままにしておくことです。これはメモリ (使用されていないときに何をするかは気にしない) には適していますが、他のリソースにはあまり適していません。それらは、実際に利用可能である場合に、利用可能としてリストされることに依存します。
  4. がないIDisposableと、非メモリ リソース (ファイルなど) は、最初にそれらを取得したオブジェクトを GC がたまたまファイナライズするまで、つまり、システムがそのオブジェクトのメモリ空間を必要としたときに利用できなくなります。それは、a) 長い間、または b) まったくない可能性があります。

ファイナライズ

  1. GC は、非決定的な順序でオブジェクトをファイナライズします。さらに悪いことに、オブジェクトのファイナライズのマネージド部分とアンマネージド部分が分割されます。
  2. つまり、GC は、管理されていない部分のに、オブジェクトの管理されている部分をファイナライズする場合があります。非管理対象が管理対象にコールバックしようとすると...

コード構造

  1. オブジェクトがいつ「放棄」されたかをコンパイラが知る良い方法はありません。
  2. ブロックは、「このUsingブロックの終了後、このオブジェクトを破棄します」とコンパイラに通知するために、変数に特殊なスコープを提供します。
  3. これにより、CLR (通常はある場所) ではなく、プログラマーで参照を管理する責任が移動します。Usingブロックからリソースを渡すと、NullReferenceException
  4. 前述の理由から、この負担は実際にはプログラマーにある必要があります。
于 2012-11-24T17:40:27.537 に答える
1

C++ 言語は、物事がいつスコープに出入りしたかを正確に追跡するように設計されています。オブジェクトが明確に識別可能な「所有者」を持っている状況では、これは非常に優れていました。残念ながら、オブジェクト自体に特定の所有者がいない状況が数多くあります。たとえば、あるオブジェクトが文字「Hello」を含む文字列を生成し、その文字列への参照を格納するFoo他のオブジェクトにそれを渡す場合、BarFooBar他のオブジェクトが文字列を必要としなくなったかどうか、またその時期を知る方法はあります。C++ は、各文字列にカウンターを関連付け、参照を作成するメソッドにそのカウンターをインクリメントさせ、参照を放棄するメソッドにそれをデクリメントさせ、カウンターがゼロになった場合はオブジェクトを削除することで、この状況に対処できます。残念ながら、メソッドがオブジェクトへの参照を渡すたびにカウンタをインクリメントおよびデクリメントする必要があると、特にマルチプロセッサ マシンではコストがかかる可能性があります (たとえば、カウントが 3 に等しく、2 つのプロセッサが同時にオブジェクトの場合、一方はカウントを 3 から 4 に、もう一方は 4 から 5 に増やします。これは、両方のプロセッサがカウントが 3 であることを認識し、両方がカウントを 4 に設定するのとは対照的です。これは惨事になります)。

ガベージ コレクション システムは、オブジェクトへの最後の参照がいつ破棄または上書きされたかを追跡することを気にせずに、単にプログラムに参照を渡すようにすることで、この問題を回避します。システムがメモリの再利用を試みる必要があると判断した場合はいつでも、すべてを一時的に凍結し、どのオブジェクトがまだそれらへのライブ参照を持っているかを調べ、不要になったオブジェクトによって使用されていたメモリを解放できます。システムがガベージ コレクション サイクルを開始することを決定したときは、すべてのプロセスを同期する必要があります (すべてを凍結する必要がある期間はシステムによって異なります)。ルーチンは参照を別のルーチンに渡します。

ガベージ コレクション システムは、メモリの管理に適しています。オブジェクトによって使用されているメモリを解放しても、解放された場合にメモリが実際に何かに使用されるまでは何の役にも立ちません。多くのメモリを使用する必要のないアプリケーションがガベージ コレクションを実行せずに、任意の長い時間実行できるという事実は良いことです。なぜなら、メモリを必要とするものが他にない場合に、オブジェクトが不必要にメモリに残っていても害がないからです。とにかく。残念ながら、アプリケーションが多くのメモリを使用していないという事実は、メモリ以外のリソースを排他的に使用し (たとえば、排他的アクセスのために開かれたファイルなど)、放棄されたオブジェクトが存在しないことを意味するものではありません。 -それらのリソースのユーザーがそれらにアクセスできないようにします。

于 2012-11-23T23:34:33.603 に答える
0

おそらく、実行時間の長い関数がある場合、または外部リソースを使用して長命クラスのフィールドを初期化した場合、変数/フィールドがスコープ外になるまで待つことはおそらくお勧めできません。

また、静的フィールドをこのように初期化しておけば、範囲外になることはありません。

于 2012-11-21T07:44:19.857 に答える