Я испытываю утечку памяти при использовании 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
назначается объектам, в отличие от духа Дельфи ... Это 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.