使用Delphi 7从(远程)计算机查询WMI时,我遇到了内存泄漏问题。内存泄漏仅发生在Windows 2003(和Windows XP 64位)上。Windows 2000没问题,Windows 2008也没问题。我想知道是否有人遇到过类似的问题。泄漏问题只发生在某些版本的Windows中,这表明可能是Windows的问题,但我在网上搜索并没有找到解决此问题的补丁。也可能是Delphi的问题,因为C#中具有类似功能的程序似乎没有这个泄漏问题。后一个事实使我相信,在Delphi中获取所需信息的另一个更好的方法而不会出现内存泄漏。
我在下面提供了暴露内存泄漏的小程序源代码。如果执行位于
通过我的快速粗略的Windows任务管理器配置文件,我发现以下内容:
您会注意到,在多个场合下,与Delphi精神相反,将
我在下面提供了暴露内存泄漏的小程序源代码。如果执行位于
{ Leak! }
注释下面的sObject.Path_
行,则会发生内存泄漏。如果我将其注释掉,就不会有泄漏。(显然,在“真正”的程序中,我会对sObject.Path_
方法调用的结果做一些有用的事情:)。通过我的快速粗略的Windows任务管理器配置文件,我发现以下内容:
Before N=100 N=500 N=1000 With sObject.Path_ 3.7M 7.9M 18.2M 31.2M Without sObject.Path_ 3.7M 5.3M 5.4M 5.3M我想问的是:还有其他人遇到过这个问题吗?如果是,它确实是Windows的问题,并且是否有热修复程序?还是(更可能)我的Delphi代码有问题,并且是否有更好的方法来获取所需信息?
您会注意到,在多个场合下,与Delphi精神相反,将
nil
赋值给对象。这些是不继承自TObject
并且没有我可以调用的析构函数的COM对象。通过将nil
分配给它们,Windows的垃圾收集器会清理它们。program ConsoleMemoryLeak;
{$APPTYPE CONSOLE}
uses
Variants, ActiveX, WbemScripting_TLB;
const
N = 100;
WMIQuery = 'SELECT * FROM Win32_Process';
Host = 'localhost';
{ Must be empty when scanning localhost }
Username = '';
Password = '';
procedure ProcessObjectSet(WMIObjectSet: ISWbemObjectSet);
var
Enum: IEnumVariant;
tempObj: OleVariant;
Value: Cardinal;
sObject: ISWbemObject;
begin
Enum := (wmiObjectSet._NewEnum) as IEnumVariant;
while (Enum.Next(1, tempObj, Value) = S_OK) do
begin
sObject := IUnknown(tempObj) as SWBemObject;
{ Leak! }
sObject.Path_;
sObject := nil;
tempObj := Unassigned;
end;
Enum := nil;
end;
function ExecuteQuery: ISWbemObjectSet;
var
Locator: ISWbemLocator;
Services: ISWbemServices;
begin
Locator := CoSWbemLocator.Create;
Services := Locator.ConnectServer(Host, 'root\CIMV2',
Username, Password, '', '', 0, nil);
Result := Services.ExecQuery(WMIQuery, 'WQL',
wbemFlagReturnImmediately and wbemFlagForwardOnly, nil);
Services := nil;
Locator := nil;
end;
procedure DoQuery;
var
ObjectSet: ISWbemObjectSet;
begin
CoInitialize(nil);
ObjectSet := ExecuteQuery;
ProcessObjectSet(ObjectSet);
ObjectSet := nil;
CoUninitialize;
end;
var
i: Integer;
begin
WriteLn('Press Enter to start');
ReadLn;
for i := 1 to N do
DoQuery;
WriteLn('Press Enter to end');
ReadLn;
end.