13

C# でオブジェクトがスコープ外になると、オブジェクトが破棄され、そのデストラクタがすぐに呼び出されることを信頼できますか?

多くの一般的なコーディング プラクティス (トランザクション オブジェクトなど) がこの動作に依存しているため、そうすべきだと思いますが、私はガベージ コレクションの操作にあまり慣れておらず、そのような言語が通常どのように動作するかについてほとんど洞察がありません。

ありがとう。

4

6 に答える 6

33

いいえ、.Net、したがって C# はガベージ コレクションのメモリ管理に依存しています。そのため、デストラクタ (.Net ではファイナライザと呼ばれます) は、GC がオブジェクトを破棄するのが適切であると判断するまで呼び出されません。

さらに、C# のほとんどの「通常の」オブジェクトにはデストラクタがありません。デストラクタ パターンが必要な場合は、Dispose Patternを使用してIDisposable インターフェイスを実装する必要があります。使い捨てオブジェクトでは、 using キーワードを使用するか、メソッドを直接呼び出してDispose メソッドが呼び出されることも確認する必要があります。

さらに (できれば) 明確にするために: 決定論的破棄は、.Net ランタイムによって管理されていないリソースを明示的に解放する必要がある場合など、.Net で役立ちます。このようなリソースの例としては、ファイル ハンドル、データベース接続などがあります。通常、これらのリソースは不要になったらすぐに解放することが重要です。したがって、GC がそれらを解放するのを待つ余裕はありません。

.Net GC の非決定論的な世界で (C++ のスコープ動作に似た) 決定論的な破棄を行うために、.Net クラスは IDisposable インターフェイスに依存しています。Dispose Patternから借用して、いくつかの例を次に示します。

まず、破棄可能なリソースをインスタンス化してからオブジェクトをスコープ外に出すと、オブジェクトの破棄は GC に任されます。

1.    {
2.       var dr = new DisposableResource();
3.    }

これを修正するために、オブジェクトを明示的に破棄できます。

1.    {
2.       var dr = new DisposableResource();
3.
4.       ...
5.
6.       dr.Dispose();
7.    }

しかし、2 行目と 6 行目の間で何か問題が発生した場合はどうなるでしょうか。Dispose は呼び出されません。例外に関係なく Dispose が最終的に呼び出されることをさらに確実にするために、次のことができます。

1.    var dr = new DisposableResource();
2.    try
3.    {
4.       ...
5.    }
6.    finally
7.    {
8.       dr.Dispose();
9.    }

このパターンはしばしば必要とされるため、C# には using キーワードが含まれており、単純化されています。次の例は、上記と同等です。

1.    using (var dr = new DisposableResource())
2.    {
3.       ...
4.    }
于 2009-09-26T09:48:52.187 に答える
13

いいえ。オブジェクトは実際には「スコープ外」にはなりません。オブジェクトへの参照(つまり、オブジェクトへのアクセスに使用する変数)はそうなります。

特定のオブジェクトへの参照がなくなると、必要に応じてそのオブジェクトがガベージコレクション(GC)の対象になります。GCが、参照されなくなったオブジェクトのスペースを再利用する必要があると判断した場合は常に、オブジェクトファイナライザーが呼び出されます。

オブジェクトがリソース(ファイルハンドル、データベース接続など)の場合、IDisposableインターフェイスを実装する必要があります(Dispose()開いている接続をクリーンアップするメソッドを実装するようにオブジェクトに義務付けます)。この場合のベストプラクティスは、オブジェクトをusingブロックの一部として作成することです。これにより、このブロックが完了すると、アプリケーションは自動的にobjectsDispose()メソッドを呼び出し、ファイル/データベース接続などを閉じます。 。

例えば


using (var conn = new DbConnection())  
{ 
   // do stuff with conn  
} // conn.Dispose() is automatically called here.  

ブロックは、オブジェクトとのusing相互作用をブロック内にラップする構文糖衣であり、呼び出しのみを行うブロックも含まれます。conntryfinallyconn.Dispose()

于 2009-09-26T09:57:37.550 に答える
4

C# には C++ のようなデストラクタはありません。(C# にはファイナライザーとも呼ばれる別の概念のデストラクタがあり、C++ デストラクタと同じ構文を使用しますが、オブジェクトの破棄とは無関係です。アンマネージ リソースのクリーンアップ メカニズムを提供することを目的としています。) ガベージ コレクタオブジェクトが参照されなくなった後、オブジェクトをクリーンアップします。すぐにではなく、これを保証する方法もありません。

幸いなことに、これを保証する本当の理由もありません。メモリが必要な場合は、GC がメモリを再利用します。そうしないと、周りにまだガベージ オブジェクトが残っていても気にする必要はありません。これはメモリ リークではありません。GC はそれを見つけて、いつでもクリーンアップできます。

于 2009-09-26T09:49:36.070 に答える
4

いいえ、これは保証されません。Java などの言語と同様に、C# ではガベージ コレクターが必要なときに実行されます (つまり、ヒープがいっぱいになったとき)。ただし、オブジェクトが を実装する場合IDisposable、i. e. メソッドがDispose()あり、それを呼び出す必要がある場合は、usingキーワードを利用できます。

using (var foo = new DisposableObject()) {
    // do something with that
}

その方法は、そのブロックDispose()を離れるとすぐに呼び出されます。using

注:IDisposableは多くのタイプで見られますが、特に GDI+ だけでなく、データベース接続、トランザクションなどにも見られるため、ここでは実際に正しいパターンである可能性があります。

注 2: 舞台裏で上記のブロックはtry/finallyブロックに変換されます。

var foo = new DisposableObject();
try
{
    // do something with that
}
finally
{
    foo.Dispose();
}

しかし、その変換はコンパイラによって行われ、 を呼び出すのを忘れないために非常に便利ですDispose()

于 2009-09-26T09:50:02.613 に答える
0

いいえ。CLI仕様(ファイナライザーについてはp。8.9.6.7)http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdfを参照すると、次のことがわかります。

CLI、インスタンスにアクセスできなくなった直後にファイナライザーが呼び出されるようにする必要があります。ファイナライズをトリガーするためにメモリプレッシャーに依存することは許容されますが、実装者は追加のメトリックの使用を検討する必要があります

しかし、そうではありません。

于 2009-09-26T09:55:36.713 に答える
0

このようにガベージコレクターに頼るべきではないと思います。彼らがどのように動作するかを差し引いても、次のリリースで再実装されている可能性が非常に高い.

いずれにせよ、オブジェクトの参照を解除した時点で、オブジェクトはガベージ コレクションされません。通常、それらはあるしきい値に達するまで収集され、その後解放されます。

特に Java プログラムでは、タスク マネージャのメモリ消費量を見ると、これは非常に顕著です。それは成長し、成長し、突然、毎分再び低下します。

于 2009-09-26T09:49:13.487 に答える