我使用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 KB每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 。