複数のスレッドで複数の C ベースの関数 (p/Invoke) を呼び出すプログラムを作成しています。
時々、アクセス違反エラーでプログラムがクラッシュします。私が最初に考えたのは、GC がメモリを最適化し、C 関数が処理していたメモリのチャンクを別の場所に移動したということでした。
私がやりたいのは、GC を機能させながら、メモリを移動 (デフラグ) する部分を無効にすることです。
これを行う方法はありますか?
複数のスレッドで複数の C ベースの関数 (p/Invoke) を呼び出すプログラムを作成しています。
時々、アクセス違反エラーでプログラムがクラッシュします。私が最初に考えたのは、GC がメモリを最適化し、C 関数が処理していたメモリのチャンクを別の場所に移動したということでした。
私がやりたいのは、GC を機能させながら、メモリを移動 (デフラグ) する部分を無効にすることです。
これを行う方法はありますか?
他の回答が言っているように、最初にすべきことは、オブジェクトを正しく固定していることを確認することです。あなたがそれをしたと仮定すると、他に何がうまくいかない可能性がありますか?
class C
{
public int handle;
...
~C() { InteropLibrary.DestroyHandle(handle); }
}
void M()
{
C c = GetSomeObjectUsefulInUnmanagedCode();
D d = InteropLibrary.UnmanagedMethodThatUsesHandle(c);
// COMMENT
d.DoSomethingWithStoredHandle();
}
COMMENT
(*)でガベージコレクションが発生した場合はどうなりますか?ガベージコレクターは、「ローカル変数c
がこのメソッドで再び参照されることはありません。積極的に処理して、デッドとして扱うことができます」と自由に言うことができます。ファイナライザーが実行され、ハンドルが破棄された場合、最後のメソッドが実行されると、破棄されたハンドルにアクセスしてクラッシュします。
このまれではあるが起こりうる問題を解決するためGC.KeepAlive
に、ガベージコレクターに特定の参照のクリーンアップにあまり積極的でないように指示するために使用できます。メソッドが終了するまで存続している場合c
は、そのデストラクタが実行できない可能性があることがわかります。
(*)もちろん、GCは別のスレッドで実行され、いつでも実行できます。GCによって中断できる操作と中断できない操作の詳細は複雑であり、正確さのためにこれらの種類の実装の詳細に依存するべきではありません。
fixed
ほとんどの場合、キーワードを使用できます。
Eric Lippert の新しいブログによると、他に少なくとも 2 つの可能性があるようです。
これら 2 つのオプションでは、メモリが正しく割り当て解除されていることを確認する必要があることに注意してください。
余談ですが、ごくわずかなメモリが移動する (アクセス違反が発生する) ことが問題である場合、解決策はメモリの移動部分全体を無効にすることはほとんどありません。
これをグローバルに実行できるとは思いませんが、fixed
キーワードを使用して特定のオブジェクトを固定し、目的の効果を得ることができます。