Использование предложения не вызывает 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, которые почти, но не совсем одинаковы.

ИЛ из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

ИЛ из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 при запросе журналов событий .

24
задан abatishchev 12 August 2012 в 08:22
поделиться