Si verifica una perdita di memoria quando si utilizza WMI da Delphi 7 per interrogare un PC (remoto). La perdita di memoria si verifica solo su Windows 2003 (e Windows XP 64). Windows 2000 va bene, così come Windows 2008. Mi chiedo se qualcuno abbia riscontrato un problema simile.Perdita di memoria tramite WMI in Delphi 7
Il fatto che la perdita si verifichi solo in determinate versioni di Windows implica che potrebbe trattarsi di un problema di Windows, ma ho cercato sul Web e non sono stato in grado di individuare un hotfix per risolvere il problema. Inoltre, potrebbe trattarsi di un problema di Delphi, dal momento che un programma con funzionalità simili in C# non sembra avere questa perdita. Quest'ultimo fatto mi ha portato a credere che potrebbe esserci un altro, migliore, modo di ottenere le informazioni di cui ho bisogno in Delphi senza ottenere una perdita di memoria.
Ho incluso la fonte in un piccolo programma per esporre la perdita di memoria di seguito. Se viene eseguita la riga sObject.Path_
sotto il commento { Leak! }
, si verifica la perdita di memoria. Se commento, non c'è nessuna perdita. (. Ovviamente, nel programma "vero", io faccio qualcosa di utile con il risultato della chiamata :) sObject.Path_
metodo)
Con un po 'veloce 'n sporco Task Manager di Windows profiling sulla mia macchina, ho trovato il seguente:
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
Credo che la mia domanda sia: qualcun altro ha riscontrato questo problema? Se è così, è davvero un problema di Windows, e c'è una correzione? O (più probabilmente) è il mio codice Delphi rotto, e c'è un modo migliore per ottenere le informazioni che mi servono?
Noterete che in diverse occasioni, nil
viene assegnato a oggetti, contrariamente allo spirito Delphi ... Questi sono oggetti COM che non ereditano da TObject
e non hanno alcun distruttore che possa chiamare. Assegnando loro nil
, il garbage collector di Windows li pulisce.
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.
Probabilmente hai ragione, ma il ripristino esplicito delle variabili ha risolto un'altra perdita di memoria. Forse sono andato un po 'fuori bordo, resettando assolutamente tutto, ma hey, le perdite di memoria non erano ancora finite :). Grazie per aver riprodotto l'errore e segnalato su di esso, però! – jqno