Using-Klausel kann Dispose nicht aufrufen?
Ich verwende Visual Studio 2010 als Ziel für das .NET 4.0-Clientprofil. Ich habe eine C # -Klasse zu erkennen, wann ein bestimmter Prozess startet / endet. Zu diesem Zweck verwendet die Klasse einen ManagementEventWatcher, der wie folgt initialisiert wird.query
, scope
undwatcher
sind Klassenfelder:
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();
Der Handler für Event EventArrived sieht folgendermaßen aus:
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");
}
}
Dieser Code basiert aufein CodeProject-Artikel. Ich habe den Anruf hinzugefügtmbo.Dispose()
weil es Speicher durchgesickert ist: Jedes Mal, wenn EventArrived ausgelöst wird, werden ca. 32 KB pro Sekunde ausgegeben. Das Leck ist sowohl bei WinXP als auch bei Win7 (64-Bit) offensichtlich.
So weit, ist es gut. Aus Gewissensgründen fügte ich hinzu:try-finally
Klausel, wie folgt:
var mbo = e.NewEvent;
try
{
eventName = mbo.ClassPath.ClassName;
}
finally
{
mbo.Dispose();
}
Kein Problem dort. Besser noch, das C #using
Klausel ist kompakter, aber äquivalent:
using (var mbo = e.NewEvent)
{
eventName = mbo.ClassPath.ClassName;
}
Toll, erst jetzt ist der Speicherleck wieder da. Was ist passiert?
Ich weiß es nicht. Aber ich habe versucht, die beiden Versionen mit ILDASM zu zerlegen, die fast, aber nicht ganz gleich sind.
IL austry-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 aususing
:
.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
Anscheinend ist das Problem diese Zeile:
IL_003c: brfalse.s IL_0044
das ist äquivalent zuif (mbo != null)
, sombo.Dispose()
wird nie gerufen. Aber wie ist es möglich, dass mbo null ist, wenn es zugreifen kann?.ClassPath.ClassName
?
Irgendwelche Gedanken dazu?
Außerdem frage ich mich, ob dieses Verhalten die ungelöste Diskussion hier erklärt:Speicherverlust in WMI beim Abfragen von Ereignisprotokollen.