У меня возникает утечка памяти при использовании WMI из Delphi 7 для запроса (удаленного) компьютера. Утечка памяти происходит только в Windows 2003 (и Windows XP 64). Windows 2000 в порядке, как и Windows 2008. Мне интересно, сталкивался ли кто-нибудь с подобной проблемой.
Тот факт, что утечка происходит только в определенных версиях 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... Это COM-объекты, которые не наследуются от TObject
и не имеют деструктора, который я мог бы вызвать. Присваивая им 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.