У меня возникает утечка памяти при использовании WMI из Delphi 7 для запроса (удаленного) ПК. Утечка памяти происходит только в Windows 2003 (и Windows XP 64). Windows 2000 в порядке, а также Windows 2008. Мне интересно, есть ли у кого-то подобная проблема.Утечка памяти с использованием WMI в Delphi 7
Тот факт, что утечка происходит только в некоторых версиях Windows, подразумевает, что это может быть проблема с Windows, но я искал в Интернете и не смог найти исправление для решения проблемы. Кроме того, это может быть проблема Delphi, поскольку программа с аналогичной функциональностью на C#, похоже, не имеет этой утечки. Последний факт заставил меня поверить, что может быть другой, лучший способ получить информацию, которая мне нужна в Delphi, без утечки памяти.
Я включил источник в небольшую программу, чтобы выявить утечку памяти ниже. Если строка sObject.Path_
ниже комментария { Leak! }
, происходит утечка памяти. Если я прокомментирую это, утечки не будет. (Очевидно, что в «реальной» программе я делаю что-то полезное в результате вызова метода sObject.Path_
:).)
С небольшим быстрым «грязным профилированием профилей Windows Task Manager» на моей машине я нашел следующее:
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.
Возможно, вы правы, но явное перенаправление переменных устранило утечку памяти. Может быть, я немного переборщил, сбросив абсолютно все, но эй, утечки памяти еще не все ушли :). Спасибо за воспроизведение ошибки и сообщения об этом, хотя! – jqno