私のアプリケーションにはクラスがあり、このクラスの各インスタンスはアンマネージリソースの一部を共有しています。共有は、managerオブジェクトによって促進されます。
インスタンスが破棄されると、割り当てられたリソースを再利用できるようにマネージャーに通知する必要があります。
プログラマーにインスタンスを明示的に破棄する責任を負わせることはできますが、管理された言語のモデルにできる限り従いたいと思います。つまり、オブジェクトが破棄の対象となる時期を判断し、自動的にマネージャーに通知するのはランタイムに任せたいのです。
問題は、マネージャーがリソースを維持するためにすべてのインスタンスのリストを必要とすることです。
管理オブジェクトへの参照を保持しながら、他のすべての参照が破棄されたときに自動的に破棄されるようにするにはどうすればよいですか?
さらに複雑なことに、この特定のアプリケーションはパフォーマンスに非常に敏感です。
単純なアプローチは、マネージャーに弱い参照でオブジェクトを追跡させることです。弱い参照を使用すると、オブジェクトへの参照を維持できますが、弱い参照によってガベージコレクションが妨げられることはありません。
var reference = new WeakReference<SomeType>(myObjectToManage);
// later
SomeType value;
if (reference.TryGetTarget(out value)) {
// the object referenced is still alive.
}
else {
// the object referenced has been garbage collected.
}
このアプローチの唯一の欠点は、オブジェクトへの参照がない場合、ガベージコレクションが発生するまで、オブジェクトがしばらく存続する可能性があることです。
別のオプションは、独自の参照カウント方式を実装することです。これには、参照が正しく追跡されるようにするために、これらのオブジェクトを使用しているクライアントコードにある程度の規律が必要です。
これはかなり広範な質問であり、明確に定義されていないパフォーマンス感度によってさらに複雑になります。ただし、基本的には、いくつかの選択肢があります。
マネージャーに工場として働き、ラッパーを配らせます。その後、ラッパーは通常のオブジェクトのように(GCを介して)収集されます。ラッパーは、基になるラップされたリソースが解放されていることをマネージャーに通知するファイナライザーを実装します。したがって、マネージャーは(ラッパーを渡すときに)カウントダウンし、(ラッパーファイナライザーを実行して)カウントダウンして、基になるリソースを解放できます。
このような単純なカウントでは、リソースが外部にあるか、少なくとも非周期的であると想定しています。
リソース間の循環が可能な場合は、追加の処理を採用する必要があります。これは、参照の定期的なスキャンなどです(たとえば、独自のミニGC)。
C#パターンusing
ステートメントとIDisposable
に適合した Resource Acquisition Is Initialization 、パターンを使用します。これはしばしばラッパーにもなります。参照 http://geekswithblogs.net/codeWithoutFear/archive/2012/06/28/raii-in-csharp.aspx
C#では、軽量ラッパーに構造体を使用できることに注意してください。ただし、ファイナライザに依存している場合は例外です。
また、他の@Erikのメモとして、C#の組み込み weak reference classes を使用できます。 MSDNから:
弱参照を使用すると、ガベージコレクターはオブジェクトを収集しながら、アプリケーションがオブジェクトにアクセスできるようになります。オブジェクトが必要な場合でも、そのオブジェクトへの強い参照を取得して、収集されないようにすることができます。短い参照と長い参照の使用方法の詳細については、「弱い参照」を参照してください。
JavaまたはC#のようなガベージコレクションされた言語には簡単な方法はありません。確定的なメモリ管理/ RAIIを取り除くことにより、リソースがメモリではなく、リソース管理を可能な限りカプセル化したい。
手動によるリソース管理の必要性を減らすために、C#にはusing ()
ブロックがあり、Javaにはtry-with-resourceがあります。実際のオブジェクトへのハンドルを表すオブジェクトを導入することもできます。 。ハンドルが閉じられると、オブジェクトの所有権はプールに戻ります。usingブロックでハンドルを作成することにより、言語がそれを処理するため、所有権を手動で解放する必要がなくなります。複雑な共有所有権の場合、これは十分ではなく、完全に手動のリソース管理に頼る必要があります。
FlyweightパターンのようなテクニックでGCシステムをハックしようとすることは、多くの場合、努力する価値がありません。そのようなソリューションに関連付けられた一時オブジェクトからの追加の間接化、簿記、および可能なガベージは、オブジェクトプールの利点を上回る可能性があります。必ずmeasureにしてください。これは、1つのソリューションを時期尚早に解決するよりも優れています。
this と this を見てみると、インスタンスの1つが破棄されるたびにマネージャーオブジェクトに通知するには、クラスにIDisposable
を実装する必要があるようです。また、マネージャーに独自の参照カウントシステムを実装する必要があります。単純なファクトリで実装する必要があります。
したがって、マネージクラスは次のようになります。
public class SharedResourceWrapper: IDisposable {
private Manager manager; // a handle to the manager
public SharedResourceWrapper(Manager manager){
this.manager = ... // allocates the resource
}
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing){
if (disposing) {
manager.RemoveReference();
}
}
}
そして、あなたのManager
クラスはこの2つのメソッドを持つべきです
public SharedResourceWrapper Create(){
var newInstance = new SharedResourceWrapper(this)
references++;
}
public void RemoveReference() {
...
reference--;
if( reference == 0){
....
}
}