Windowsサービスとして実行される継承された.NET 4.0アプリケーションがあります。私は.NETのエキスパートではありませんが、30年以上のコードを書いた後、自分の道を見つける方法を知っています。.NET4 ExpandoObjectの使用メモリが漏れる
サービスが最初に起動すると、約70MBのプライベートワーキングセットでクロックインします。サービスが実行される時間が長ければ長いほど、メモリが増えます。この増加は、座って見ている間に気付くほど劇的ではありませんが、アプリケーションが長時間(100日以上)実行された後、複数のGB(現在のレコードは5GB)までです。実行中のインスタンスにANTS Memory Profilerを接続し、ExpandoObjectの使用がGCによってクリーンアップされない複数メガバイトの文字列を占めるように見えることがわかりました。おそらく他の漏れがありますが、これが最も目立つので最初に攻撃されました。
他のSO投稿から、ExpandoObjectの「通常の」使用法が、動的に割り当てられた属性を読み込む(書き込みはしない)ときに内部RuntimeBinderExceptionを生成することを知りました。
dynamic foo = new ExpandoObject();
var s;
foo.NewProp = "bar"; // no exception
s = foo.NewProp; // RuntimeBinderException, but handled by .NET, s now == "bar"
あなたは例外はVisualStudioをで起こる見ることができますが、最終的にはそれは.NETの内部で処理されますし、あなたが戻って取得するすべての必要な値です。
例外...例外のMessageプロパティの文字列は、ヒープ上にとどまるように見え、生成されたExpandoObjectが範囲外になった後でさえも、ガーベジコレクションされません。
簡単な例:
using System;
using System.Dynamic;
namespace ConsoleApplication2
{
class Program
{
public static string foocall()
{
string str = "", str2 = "", str3 = "";
object bar = new ExpandoObject();
dynamic foo = bar;
foo.SomePropName = "a test value";
// each of the following references to SomePropName causes a RuntimeBinderException - caught and handled by .NET
// Attach an ANTS Memory profiler here and look at string instances
Console.Write("step 1?");
var s2 = Console.ReadLine();
str = foo.SomePropName;
// Take another snapshot here and you'll see an instance of the string:
// 'System.Dynamic.ExpandoObject' does not contain a definition for 'SomePropName'
Console.Write("step 2?");
s2 = Console.ReadLine();
str2 = foo.SomePropName;
// Take another snapshot here and you'll see 2nd instance of the identical string
Console.Write("step 3?");
s2 = Console.ReadLine();
str3 = foo.SomePropName;
return str;
}
static void Main(string[] args)
{
var s = foocall();
Console.Write("Post call, pre-GC prompt?");
var s2 = Console.ReadLine();
// At this point, ANTS Memory Profiler shows 3 identical strings in memory
// generated by the RuntimeBinderExceptions in foocall. Even though the variable
// that caused them is no longer in scope the strings are still present.
// Force a GC, just for S&G
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.Write("Post GC prompt?");
s2 = Console.ReadLine();
// Look again in ANTS. Strings still there.
Console.WriteLine("foocall=" + s);
}
}
}
"バグ" はbeholdersの目にある、私は考えます(私の目には、バグを言います)。何か不足していますか?これは正常であり、グループの.NETマスターが期待していますか?物事をクリアするためにそれを伝える方法はありますか?最初に動的/ ExpandoObjectを使用しないことをお勧めしますか?
まだ存在漏れているのですか? –
はい、ただし、どのスレッドが何回発火したかにかかわらず、例外文字列の3つのインスタンスしか表示されません。 – AngryPrimate