我有一個作爲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);
}
}
}
「錯誤」 是在旁觀者的眼睛,我想(我的眼睛說的bug)。我錯過了什麼嗎?這是否正常,並且是由組中的.NET大師預期的?有什麼辦法可以告訴它清除所有的東西嗎?首先不使用動態/ ExpandoObject是最好的方法嗎?
? –
是的,但它只顯示異常字符串的3個實例,無論我發射多少個線程。 – AngryPrimate