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

Ответы на вопрос(3)

Ваш ответ на вопрос