泄漏仅发生在某些版本的Windows中这意味着它可能是一个Windows问题,但我一直在搜索网络,但无法找到解决此问题的修补程序.此外,它可能是一个Delphi问题,因为在C#中具有类似功能的程序似乎没有这种泄漏.后一个事实使我相信可能有另一种更好的方法来获取Delphi中我需要的信息而不会导致内存泄漏.
我已将源代码包含在一个小程序中,以揭示下面的内存泄漏.如果行sObject.Path_低于{Leak!执行注释,发生内存泄漏.如果我发表评论,那就没有泄漏. (显然,在“真正的”程序中,我使用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代码被破坏了,有没有更好的方法来获取我需要的信息?
您会注意到,有几次将nil分配给对象,这与Delphi精神相反……这些是不从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,'',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.
解决方法
不过,我想评论一下您重置所有变量和接口变量.你写
You’ll notice on several occasions,nil is assigned to objects,contrary to the Delphi spirit… These are COM objects that do not inherit from TObject,and have no destructor I can call. By assigning nil to them,Windows’s garbage collector cleans them up.
这不是真的,因此不需要将变量设置为nil和Unassigned. Windows没有垃圾收集器,你正在处理的是引用计数对象,一旦引用计数达到0就会立即销毁.Delphi编译器会根据需要插入必要的调用来递增和递减引用计数.您对nil和Unassigned的赋值会减少引用计数,并在对象达到0时释放该对象.
对变量的新赋值或者退出过程也会解决这个问题,因此额外的赋值(尽管不是错误的)是多余的,并且会降低代码的清晰度.以下代码完全等效,不会泄漏任何额外的内存:
procedure ProcessObjectSet(WMIObjectSet: ISWbemObjectSet); var Enum: IEnumVariant; tempObj: OleVariant; Value: Cardinal; sObject: ISWbemObject; begin Enum := (wmiObjectSet._NewEnum) as IEnumVariant; while (Enum.Next(1,Value) = S_OK) do begin sObject := IUnkNown(tempObj) as SWBemObject; { Leak! } sObject.Path_; end; end;
我会说只有当这实际上释放了对象时才显式重置接口(因此当前引用计数必须为1)并且破坏本身应该恰好在此时发生.后者的示例是可以释放大块内存,或者需要关闭文件或释放同步对象.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。