2010-06-29 134 views
11

我有一個複雜的應用程序,我剛剛介紹了一些更改,在接口中添加了一些新類並刪除了其他一些類。在功能上它可以正常工作,但是在類的Destroy過程之後,我得到一個訪問衝突:如何在Delphi中找到導致AV的懸掛接口

「地址0040B984在模塊'xxxx.exe'中的訪問衝突。地址80808088的讀取」。

我知道這是在該類的'Finalize'代碼中,如果我進入反彙編(Delphi 2010),我可以看到AV的重點。我看不到一個簡單的方法來找出哪些變量觸發了這個。在進行深入研究時,有沒有一種程序可以幫助我找到正在提交的實例?

感謝 布賴恩

+2

我建議的免費使用,而不是破壞。 – Bharat 2010-06-29 09:27:53

回答

13

這個錯誤看起來像你正在使用FastMM內存管理。 該錯誤表明您正在引用已由FastMM以DebugFillDWord值清除的指針。

這意味着您正在使用引用已被釋放的對象的接口。
這也意味着你還沒有啓用CatchUseOfFreedInterfaces

爲了改變這些,並進行調試,你不能使用Delphi自帶的FastMM股票。
您需要下載FastMM(版本4.94)。

下載後:

gabr已經提到了,裏面FastMM4Options.inc,請確保您啓用FullDebugModeCatchUseOfFreedInterfaces(即禁用CheckUseOfFreedBlocksOnShutdown,但你現在不感興趣,後者)。
您可能還想啓用RawStackTraces;這取決於你的當前堆棧跟蹤是否足夠好。

當你完成這些設置,然後通過調試運行與FastMM您的應用程序,並把一個斷點這種方法FastMM4單元內:

procedure TFreedObject.InterfaceError; 

我已經修改了我的FastMM4單元有點讓我更多的上下文信息;我可以與你分享(我已經將它郵寄給FastMM4團隊,但尚未包含在官方消息中)。

我寫了一個非常密集的blog article on debugging using FastMM,可能會幫助你。
如果需要進一步解釋,請在此處放一張紙條:-)

祝您好運,如果您需要更進一步的指示,請告訴我們。

--jeroen

編輯:20100701 - 強調布賴恩的評論中提到的位。

+0

謝謝,我會很感激你提到的上下文信息模式。已經在這裏使用FastMM 494,但我會繼續關注您的博客步驟。 Bri – 2010-06-29 11:41:40

+0

@brian:通過我的博客(或電子郵件給我留言;幾乎任何在pluimers.com上的作品,尤其是當你在登錄前使用我的名字時) – 2010-06-29 11:56:30

+1

@All:這個答案的關鍵點是: 「完成這些設置後,通過調試器使用FastMM運行應用程序,並在FastMM4單元內的此方法上放置一個斷點:procedure TFreedObject.InterfaceError」。在FastMM中,確定有關所產生的TFreedObject(即它的類類型)的更多信息的某種方法將是v.useful。 – 2010-06-30 14:02:41

13

在大多數情況下,這樣的錯誤可以通過使用FastMM和編譯有條件的定義FullDebugModeCatchUseOfFreedInterfaces應用程序被捕獲。只要確保你把FastMM4放在dpr的'用途'列表的第一位。

+1

好的建議gabr,我正在使用FastMM,但沒有打開'CatchUseOfFreedInterfaces'。現在,這給了我一大堆我以前正在追蹤的步驟,但我並沒有更清楚地知道'誰持有'這個非法參考。我正在做我自己的裁判計數(-1),因爲我釋放了我自己的類(就像TComponent一樣),所以我需要知道非法接口指針從哪裏得到。 Bri – 2010-06-29 11:09:29

+0

幾周前,我遇到了一個類似的問題,它是由混合引用和非引用計數接口引起的。我不確定爲什麼這會造成問題,但從來沒有這麼做過。 – Vegar 2010-06-29 20:42:51

6

步驟來發現問題:

  1. 使用FastMM在fulldebugmode爲賈布爾建議(我想你已經做的,看着808080模式)。
  2. 設置你在課堂上使用明確爲nil在銷燬過程
  3. 所有接口放置一個斷點在你開始銷燬程序
  4. 現在通過你的毀滅過程中的步驟,nilling懸空接口時,你會得到你訪問衝突,你會知道它是哪個接口。
  5. 如果在將所有接口無問題後仍然保留AV,請執行第2步至第5步以獲取父類。

我也有過這些問題,上述方法幫助我找到它們。我的問題是由實現接口的TComponents引起的。假設你有ComponentA和ComponentB,ComponentB實現了一個接口。將ComponentB(或其接口)分配給ComponentA並存儲接口引用。現在ComponentB被破壞,但ComponentA不知道這一點。當你銷燬ComponentA時,它會刪除接口,調用_Release方法並獲得AV。

解決方案是使用TComponent.FreeNotification。當您收到來自ComponentB的免費通知時,您無法使用ComponentA中的接口。我對你的代碼一無所知,但如果你的問題類似,你也可以使用FreeNotifications。

編輯:增加的步驟5

+0

福克斯:我會仔細檢查我到處都有nils! – 2010-06-29 11:42:14

+0

@Brian Frost:你甚至知道哪個班正在製作AV嗎?明確地將接口設置爲nil非常簡單,但是當擁有大量接口時會非常麻煩。 – 2010-06-29 11:51:54

2

一件事在你的代碼是尋找這個

FInterfacedObject.GetInterface 
在同一範圍

FInterfacedObject := TInterfacedObjectClass.Create. 

其中FInterfacedObject是一個類變量。

如果您願意,可以從內部函數調用GetInterface,但如果您在與創建FInterfacedObject相同的範圍內調用GetInterface,無論出於何種原因,您將引用計數爲0並釋放該事物,但它將不會是零,所以如果你做

if assigned(FInterfacedObject) then 
    FInterfacedObject.Free; 

你會得到一個訪問衝突。

3

一個類似的bug已經咬了我一個接口引用已經設置在一個現有的對象,接口引用計數器不會自動減少所有者對象被釋放。它可以通過所有者對象的析構函數中的if Assigned(FMyInterface) then FMyInterface := nil;來解決。

+0

明智的教訓:如果您使用接口,請獨佔使用它們並讓refcounting機制爲您管理所有內容。 儘可能避免TComponent後代上的接口,因爲所有者/組件關係將與導致一個受害者的引用計數機制競爭:試圖調試內存問題的人。 – 2010-06-29 20:46:36

3

我做類似的事情,在你的對象的析構函數下面的代碼將有助於

Destructor TMyObjectThatIAmDoingManualRefCounting.Destroy; 
begin 
    if FMyRefCount<>0 then 
    messageDlg('You dork, you called Free on me when someone still had references to me'); 

    inherited; 
end; 

那麼你至少可以找出你innapropriately釋放對象。可能你太早釋放它了。釋放對象太早實際上不會導致問題,它是當你釋放持有對已經釋放的對象的接口引用的對象時,你將得到錯誤。

您可以做的另一件事是在您的addref和release方法中放置斷點,並跟蹤誰在保持接口引用以及這些相同對象是否隨後釋放它們。

也是一個常見問題如下,如果你得到一個接口,並釋放對象相同的方法

var 
    o:TSomeObject; 
begin 
    o:=TSomeObject.Create; 
    (o as ISomeInterface).DoSomething; 
    o.free 
end; 

這將導致在該方法的最後一個AV,因爲編譯器會創建一個假的在方法結束時釋放的接口變量。

,你需要做的這個

var 
    o:TSomeObject; 
    i:ISomeInterface; 
begin 
    o:=TSomeObject.Create; 
    i:=(o as ISomeInterface); // or Supports or whatever 
    i.DoSomething; 
    i:=nil; 
    o.free 
end; 
相關問題