سؤال

وأنا تعاني من تسرب الذاكرة عند استخدام WMI من دلفي 7 الاستعلام (البعيد) الكمبيوتر. حدوث تسرب للذاكرة فقط على ويندوز 2003 (ويندوز XP 64). ويندوز 2000 على ما يرام، وهكذا هو ويندوز 2008. وأنا أتساءل إذا كان أي شخص قد شهدت مشكلة مماثلة.

والحقيقة أن تسرب يحدث فقط في إصدارات معينة من Windows يعني أنه قد يكون مسألة يندوز، ولكن لقد تم البحث في الإنترنت ولم تكن قادرا على تحديد موقع الإصلاح لحل هذه المسألة. أيضا، قد يكون مسألة دلفي، لأن البرنامج مع وظائف مماثلة في C # لا يبدو أن يكون هذا التسرب. وقد قادني حقيقة الأخير إلى الاعتقاد بأنه قد يكون هناك آخر، أفضل طريقة للحصول على المعلومات التي أحتاجها في دلفي دون الحصول على تسرب الذاكرة.

ولقد تضمنت المصدر إلى برنامج صغير لكشف تسرب الذاكرة أدناه. إذا تم تنفيذ 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

وأعتقد سؤالي هو: من أي شخص آخر واجهت هذه المشكلة؟ إذا كان الأمر كذلك، هل هو بالفعل قضية ويندوز، وهناك الإصلاح؟ أو (على الأرجح) هو قانون بلدي دلفي كسر، وليس هناك طريقة أفضل للحصول على المعلومات التي احتاجها؟

وستلاحظ في عدة مناسبات، يتم تعيين nil إلى الأشياء، وتتعارض مع روح دلفي ... هذه هي كائنات COM التي لا ترث من TObject، وليس لديهم المدمر يمكن أن أدعو. عن طريق تعيين nil لهم، جامع القمامة ويندوز وينظف لهم.

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.
هل كانت مفيدة؟

المحلول

ويمكنني استخراج السلوك، رمز تسرب الذاكرة على نظام التشغيل Windows XP 64 و لا على نظام التشغيل Windows XP. ومن المثير للاهتمام يحدث هذا فقط إذا تم قراءة خاصية Path_ والقراءة Properties_ أو Security_ مع نفس رمز لا تسرب أي ذاكرة. مشكلة ويندوز الإصدار محددة في WMI يشبه السبب الأكثر احتمالا من هذا. نظام بلدي هو ما يصل إلى التاريخ AFAIK، لذلك هناك ربما ليس الإصلاح لهذا أيضا.

وأود أن أعلق على هاتفك إعادة جميع المتغيرات البديل واجهة، وبالرغم من ذلك. تكتب

<اقتباس فقرة>   

وستلاحظ في عدة مناسبات، يتم تعيين شيء إلى الأشياء، وتتعارض مع روح دلفي ... هذه هي كائنات COM التي لا ترث من TObject، وليس لديهم المدمر يمكن أن أدعو. عن طريق تعيين شيء لهم، جامع القمامة ويندوز وينظف لهم.

وهذا غير صحيح، وبالتالي ليست هناك حاجة لضبط المتغيرات لnil وUnassigned. ويندوز لايوجد أحد هواة جمع القمامة، ما كنت تتعامل مع كائنات-عد المرجعية، التي يتم تدميرها فورا بمجرد عدد مرجع يصل 0. دلفي مترجم لا تضاف المكالمات اللازمة لزيادة وإنقاص عد المرجع عند الضرورة. المهام الخاصة بك إلى nil وUnassigned تنقيص عدد مرجع، وتحرير الكائن عندما يصل إلى 0.

وهناك مهمة جديدة إلى متغير، أو مغادر الإجراء رعاية هذا كذلك، وتحديد المهام إضافية حتى الآن (وإن لم يكن خطأ) لزوم لها وتقلل من وضوح التعليمات البرمجية. التعليمة البرمجية التالية يعادل تماما ولا تسرب أي ذاكرة إضافية:

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_;
  end;
end;

وانا اقول ينبغي لأحد أن إعادة اجهات صراحة إلا إذا كان هذا لا فعلا تحرير كائن (حتى العد المرجع الحالي يجب أن يكون 1) وتدمير نفسه يجب أن يحدث حقا بالضبط في هذه المرحلة. أمثلة لهذه الأخيرة هي أن جزءا كبيرا من الذاكرة يمكن تحرير، أو أن ملف يحتاج إلى أن تكون مغلقة أو كائن مزامنة أن يطلق سراحه.

نصائح أخرى

ويجب تخزين قيمة الإرجاع

sObject.Path_;

وفي متغير وجعلها SWbemObjectPath. وهذا ضروري لجعل الحق حساب مرجع.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top