2011-08-07 10 views
5
unit Unit7; 

interface 

uses Classes; 

type 
    TListener = class(TThread) 
    procedure Execute; override; 
    end; 

    TMyClass = class 
    o1,o2: Tobject; 
    procedure FreeMyObject(var obj: TObject); 
    constructor Create; 
    destructor Destroy; override; 
    end; 

implementation 

uses Windows, SysUtils; 

var l: TListener; 
    my: TMyClass; 

procedure TListener.Execute; 
var msg:TMsg; 
begin 
    while(GetMessage(msg, Cardinal(-1), 0, 0)) do 
    if(msg.message=6) then begin 
     TMyClass(msg.wParam).FreeMyObject(TObject(msg.lParam)); 
     Exit; 
    end; 
end; 

constructor TMyClass.Create; 
begin 
    inherited; 
    o1:=TObject.Create; 
    o2:=Tobject.Create; // Invalid pointer operation => mem leak 
end; 

destructor TMyClass.Destroy; 
begin 
    if(Assigned(o1)) then o1.Free; 
    if(Assigned(o2)) then o2.Free; 
    inherited; 
end; 

procedure TMyClass.FreeMyObject(var obj: TObject); 
begin 
    FreeAndNil(obj); 
end; 

initialization 
    l:= TListener.Create(); 
    my:=TMyClass.Create; 

    sleep(1000); //make sure the message loop is set 
    PostThreadMessage(l.ThreadID, 6, Integer(my), Integer(my.o2)); 
finalization 
    l.Free; 
    my.Free; 
end. 

मैंने अपनी समस्या का वर्णन करने के लिए संदेश हैंडलर का उपयोग किया है, इसलिए आप इसे समझते हैं। असली डिजाइन बहुत जटिल है। फ़ंक्शन 'FreeMyObject' वास्तव में मुक्त होता है और पॉलीमोर्फिज्म प्रतिमान का उपयोग करके एक उदाहरण बनाता है, लेकिन इसकी आवश्यकता नहीं है। मैं केवल यह इंगित करना चाहता हूं कि डिज़ाइन वही रहना चाहिए।क्यों एक मेम रिसाव है और इसे कैसे ठीक किया जाए?

अब प्रश्न और समस्या - ऐसा क्यों होता है और इसे कैसे ठीक किया जाए? ऐसा लगता है कि 'अगर असाइन किया गया (ओ 2)' फिट नहीं है।

मुझे क्या लगता है: my.o2 पर पॉइंटर भेजना मुक्त होगा और शून्य ओ 2 और मैं ऐसा करने की कोशिश करता हूं, लेकिन मैं संदेश हैंडलर में ऑब्जेक्ट से ऑब्जेक्ट में कनवर्ट नहीं कर सका, मुझे कोई जानकारी नहीं मिली।

कोई भी हाथ दे सकता है? धन्यवाद

+0

'करता अमान्य सूचक आपरेशन => मेम वास्तव में leak' लाइन में हैं o2': = Tobject.Create; ' – mjn

+0

@mjn। यह शायद विनाशक में इसी पंक्ति से संबंधित है। :) – GolezTrol

+0

संदेश संख्याओं के लिए आप WM_APP से अधिक मूल्यों का बेहतर उपयोग करेंगे। 6 WM_ACTIVATE है, और परेशानी का कारण बन सकता है। – GolezTrol

उत्तर

6

आप दो बार o2 मुक्त करते हैं। एक बार संदेश के परिणामस्वरूप और एक बार विनाशक से।

आपको लगता है कि आप o2nil पर सेट कर रहे हैं जब आप FreeMyObject पर कॉल करते हैं लेकिन आप नहीं हैं। आप वास्तव में msg.lParam को 0

o2 एक ऑब्जेक्ट का संदर्भ रखने वाला एक चर है। आप o2 के मान को पारित कर रहे हैं और जब आप मूल्य से गुजरते हैं तो आप उस वैरिएबल को संशोधित नहीं कर सकते जिसका मूल्य आपने पारित किया है। इसलिए आपको o2 पर संदर्भ पारित करने की आवश्यकता है। ताकि आप पुनर्निर्देशन का एक अतिरिक्त स्तर इतना तरह o2 के लिए सूचक जोड़ने के लिए और पारित करने के लिए, की जरूरत है ऐसा करने के लिए:

if(msg.message=6) then begin 
    FreeAndNil(PObject(msg.lParam)^); 
    Exit; 
end; 

... 

PostThreadMessage(l.ThreadID, 6, 0, LPARAM(@my.o2)); 

आप FreeMyObject की जरूरत नहीं है, तो आप सिर्फ सीधे FreeAndNil कॉल कर सकते हैं। और आपको संदेश में एक उदाहरण पास करने की आवश्यकता नहीं है।

मुझे आशा है कि आपका असली कोड उतना अजीब नहीं है जितना! ;-)

+1

पोस्ट थ्रेड मैसेज में अनुशंसित कलाकार जो पिछड़ा और भविष्य का सबूत है (64 बिट) पोस्ट थ्रेड मैसेज (एल। थ्रेड आईडी, 6,0, एलपीराम (@ my.o2)) है; –

+0

@ एलयू आरडी धन्यवाद, आप बिल्कुल सही हैं, मैं इसे अपडेट कर दूंगा, मुझे पता होना चाहिए कि चूंकि मेरा खुद का कोड बस कुछ महीने पहले ही कहता है !! –

+0

हाँ, पहले से ही यह कोशिश की और विफल रहा। समस्या कहीं और है। बी = कक्षा (ए) चलो। ए में यह संदेश भेजा जाता है जहां lparam = @ self - इसे काम करना चाहिए। लेकिन नहीं होगा। @ स्वयं उस पते को इंगित नहीं करेगा जहां ए के संदर्भ को रखा गया है। अफसोस की बात है, जब तक मुझे यह पता चला कि मैं 2 दिन खो गया। प्रयास के लिए धन्यवाद ', स्वीकार किया! – netboy

1

यहां क्या हो रहा है:

प्रोग्राम शुरू होता है। प्रारंभिकता धागे को एक संदेश भेजती है और भेजती है, जो संदर्भ में FreeAndNil पर संदर्भित होता है। यह संदर्भ शून्य में पारित होने वाला संदर्भ सेट करता है, लेकिन यह o2शून्य पर ऑब्जेक्ट फ़ील्ड सेट नहीं करता है। यह एक अलग संदर्भ है।

फिर विनाशक में, क्योंकि क्षेत्र शून्य नहीं है, यह इसे फिर से मुक्त करने का प्रयास करता है और आपको डबल-फ्री त्रुटि (अमान्य पॉइंटर ऑपरेशन अपवाद) मिलता है। चूंकि आपने विनाशक में अपवाद उठाया है, इसलिए टीएम क्लास कभी नष्ट नहीं होता है और आपको इससे स्मृति रिसाव मिल जाता है।

यदि आप यह सही करना चाहते हैं, तो किसी संदर्भ के बजाय FreeMyObject पर किसी प्रकार का पहचानकर्ता पास करें। एक पूर्णांक 2, या एक स्ट्रिंग o2 की तरह। उसके बाद FreeMyObject इस मान का उपयोग करके देखें कि इसे FreeAndNil पर कॉल करना चाहिए। (यदि आपके पास डेल्फी 2010 या बाद में है, तो आरटीटीआई के साथ यह करना बहुत आसान है।) यह थोड़ा और काम है, लेकिन यह आपके द्वारा देखी जा रही त्रुटियों को ठीक करेगा।

+0

आरटीटीआई शीर्ष पर है, बस चर के लिए एक सूचक की जरूरत है। –

+0

@ डेविड: आपके उत्तर में encapsulation उल्लंघन की संभावना मुझे डराता है ...: पी –

+0

यह असली कोड नहीं है? एक स्ट्रिंग को कैसे गुजरता है, जो किसी भी तरह से काम नहीं करता है क्योंकि यह marshalled नहीं मिलता है, और आरटीआई वर्ग का उपयोग महान encapsulation के रूप में ?! –

3

यदि आप FreeAndNil ऑब्जेक्ट संदर्भ Integer(my.o2) भेजना चाहते हैं तो ऑब्जेक्ट पर्याप्त नहीं है - आपको Integer(@my.o2) की आवश्यकता है। आपको अपने कोड में भी इसी तरह के बदलाव करना चाहिए।

के बाद से अपने कोड डिबग के लिए मुश्किल है मैं एक साधारण डेमो लिखा है आवश्यक कोड में परिवर्तन की एक विचार देने के लिए:

type 
    PObject = ^TObject; 

procedure FreeObj(PObj: PObject); 
var 
    Temp: TObject; 

begin 
    Temp:= PObj^; 
    PObj^:= nil; 
    Temp.Free; 
end; 

procedure TForm17.Button1Click(Sender: TObject); 
var 
    Obj: TList; 
    PObj: PObject; 

begin 
    Obj:= TList.Create; 
    PObj:= @Obj; 
    Assert(Obj <> nil); 
    FreeObj(PObj); 
    Assert(Obj = nil); 
end; 
संबंधित मुद्दे

 संबंधित मुद्दे