Tengo una pérdida de memoria cuando uso WMI desde Delphi 7 para consultar una pc (remota). La pérdida de memoria solo ocurre en Windows 2003 (y Windows XP 64). Windows 2000 está bien, al igual que Windows 2008. Me pregunto si alguien ha experimentado un problema similar.Fuga de memoria con WMI en Delphi 7
El hecho de que la filtración solo ocurra en ciertas versiones de Windows implica que podría tratarse de un problema de Windows, pero he estado buscando en la web y no he podido encontrar una revisión para resolver el problema. Además, podría ser un problema de Delphi, ya que un programa con una funcionalidad similar en C# no parece tener esta fuga. El último hecho me ha llevado a creer que podría haber otra forma mejor de obtener la información que necesito en Delphi sin tener una fuga de memoria.
He incluido la fuente en un programa pequeño para exponer la fuga de memoria a continuación. Si se ejecuta la línea sObject.Path_
debajo del comentario { Leak! }
, se produce la pérdida de memoria. Si lo comento, no hay filtración. (Obviamente, en el programa "real", hago algo útil con el resultado de la llamada al método sObject.Path_
:).)
Con un poco de rápido y sucio perfil del Administrador de tareas de Windows en mi máquina, encontré lo siguiente:
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
Supongo que mi pregunta es: ¿Alguien más ha encontrado este problema? Si es así, ¿es de hecho un problema de Windows y hay una revisión? O (más probable) es mi código Delphi roto, y ¿hay una mejor manera de obtener la información que necesito?
Notarás en varias ocasiones que nil
se asigna a objetos, contrariamente al espíritu Delphi ... Estos son objetos COM que no heredan de TObject
, y no tienen un destructor al que pueda llamar. Al asignarles nil
, el recolector de basura de Windows los limpia.
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.
Probablemente tenga razón, pero al restablecer explícitamente las variables se resolvió otra pérdida de memoria. Tal vez fui un poco por la borda al restablecer absolutamente todo, pero bueno, las pérdidas de memoria no habían desaparecido todavía :). ¡Gracias por reproducir el error y reportarlo! – jqno