Windows 서비스로 실행되는 상속 된 .NET 4.0 응용 프로그램이 있습니다. 어떤 스트레칭으로 .NET 전문가가 아니지만 30 년 이상 코드를 작성한 후에는 내 길을 찾을 수있는 방법을 알고 있습니다..NET4 ExpandoObject 사용 메모리 누수
서비스가 처음 시작될 때 약 70MB 개인 작업 세트에서 시계가 시작됩니다. 서비스가 길어질수록 더 많은 메모리가 사용됩니다. 이 증가는 앉아서보고있는 동안 눈치 채지 못했지만 응용 프로그램을 오랜 시간 (100 일 이상) 실행 한 후에는 최대 여러 GB (현재 레코드가 5GB) 인 경우를 보았습니다. ANTS 메모리 프로파일 러를 실행중인 인스턴스에 연결하여 ExpandoObject의 사용이 GC로 정리되지 않은 여러 메가 바이트의 문자열을 차지하는 것으로 나타났습니다. 다른 누출 가능성이 있지만, 가장 눈에 띄기 때문에 먼저 공격을당했습니다.
나는 ExpandoObject의 "일반적인"사용이 동적으로 할당 된 속성을 읽을 때 (그러나 쓰지는 않음) 내부 RuntimeBinderException을 생성한다는 것을 다른 SO 게시물에서 알게되었습니다.
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);
}
}
}
"버그"는 보는 사람의 눈에, 나는 가정 (내 눈이 버그를 말한다). 내가 놓친 게 있니? 그룹의 .NET 마스터가 정상적이고 기대합니까? 물건을 치우라고 말할 수있는 방법이 있습니까? 동적/ExpandoObject를 처음부터 사용하지 않는 가장 좋은 방법은 무엇입니까?
:
ExpandoObject
이IDictionary<string, object>
그래서 다음과 같은 작은 재 작성이 동일한 출력을 생성하지만, 동적 코드의 오버 헤드를 방지 구현합니다? –예,하지만 내가 얼마나 많은 스레드를 끄고 관계없이 예외 문자열의 3 인스턴스를 보여줍니다. – AngryPrimate