Использование предложения не вызывает Dispose?
Я использую Visual Studio 2010 для настройки профиля клиента .NET 4.0. У меня есть класс C #, чтобы определить, когда данный процесс начинается / заканчивается. Для этого класс использует ManagementEventWatcher, который инициализируется, как показано ниже;query
, scope
а такжеwatcher
поля класса:
query = new WqlEventQuery();
query.EventClassName = "__InstanceOperationEvent";
query.WithinInterval = new TimeSpan(0, 0, 1);
query.Condition = "TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'notepad.exe'";
scope = new ManagementScope(@"\\.\root\CIMV2");
watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += WatcherEventArrived;
watcher.Start();
Обработчик события EventArrived выглядит так:
private void WatcherEventArrived(object sender, EventArrivedEventArgs e)
{
string eventName;
var mbo = e.NewEvent;
eventName = mbo.ClassPath.ClassName;
mbo.Dispose();
if (eventName.CompareTo("__InstanceCreationEvent") == 0)
{
Console.WriteLine("Started");
}
else if (eventName.CompareTo("__InstanceDeletionEvent") == 0)
{
Console.WriteLine("Terminated");
}
}
Этот код основан настатья CodeProject, Я добавил звонокmbo.Dispose()
из-за утечки памяти: около 32 КБ каждый раз, когда вызывается EventArrived, раз в секунду. Утечка очевидна как для WinXP, так и для Win7 (64-разрядная версия).
Все идет нормально. Пытаясь быть добросовестным, я добавилtry-finally
оговорка, как это:
var mbo = e.NewEvent;
try
{
eventName = mbo.ClassPath.ClassName;
}
finally
{
mbo.Dispose();
}
Нет проблем там. Еще лучше, C #using
предложение более компактно, но эквивалентно:
using (var mbo = e.NewEvent)
{
eventName = mbo.ClassPath.ClassName;
}
Отлично, только теперь утечка памяти вернулась. Что случилось?
Ну, я не знаю. Но я попытался разобрать две версии с ILDASM, которые почти, но не совсем одинаковы.
IL изtry-finally
:
.try
{
IL_0030: nop
IL_0031: ldloc.s mbo
IL_0033: callvirt instance class [System.Management]System.Management.ManagementPath [System.Management]System.Management.ManagementBaseObject::get_ClassPath()
IL_0038: callvirt instance string [System.Management]System.Management.ManagementPath::get_ClassName()
IL_003d: stloc.3
IL_003e: nop
IL_003f: leave.s IL_004f
} // end .try
finally
{
IL_0041: nop
IL_0042: ldloc.s mbo
IL_0044: callvirt instance void [System.Management]System.Management.ManagementBaseObject::Dispose()
IL_0049: nop
IL_004a: ldnull
IL_004b: stloc.s mbo
IL_004d: nop
IL_004e: endfinally
} // end handler
IL_004f: nop
IL изusing
:
.try
{
IL_002d: ldloc.2
IL_002e: callvirt instance class [System.Management]System.Management.ManagementPath [System.Management]System.Management.ManagementBaseObject::get_ClassPath()
IL_0033: callvirt instance string [System.Management]System.Management.ManagementPath::get_ClassName()
IL_0038: stloc.1
IL_0039: leave.s IL_0045
} // end .try
finally
{
IL_003b: ldloc.2
IL_003c: brfalse.s IL_0044
IL_003e: ldloc.2
IL_003f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0044: endfinally
} // end handler
IL_0045: ldloc.1
Видимо проблема в этой строке:
IL_003c: brfalse.s IL_0044
что эквивалентноif (mbo != null)
, такmbo.Dispose()
никогда не называется. Но как это возможно для mbo быть нулевым, если он был в состоянии получить доступ.ClassPath.ClassName
?
Есть мысли по этому поводу?
Кроме того, мне интересно, помогает ли это поведение объяснить неразрешенную дискуссию здесь:Утечка памяти в WMI при запросе журналов событий.