6

我有这个讨厌的错误,过去消失了,但现在过了一段时间又回来了。

我创建了两个 TSam 对象(从 TPersistent 派生)并将其加载到一个 TAsmJob 对象(从 TObjectList 派生)。

在运行时,一个表单会创建一个 TStringGrid,然后是创建这两个 SAM 对象的 AsmJob(并在每个对象中从磁盘加载一些数据)。AsmJob 也分配给网格。当表单被销毁时,Grid 通过释放 AsmJob 来处理它,从而释放 TSam 对象。这是问题所在:第一个对象被释放而没有问题,但第二个对象在调用其继承方法(在 Destroy 析构函数中)时死亡。

我在整个程序中使用 FreeAndNil 来释放对象。TSam 对象不是 NIL !!!!!!因此,这是释放对象的第一次尝试。甚至对象内部的数据也是一致的。

该程序的主干如下所示:

**Create:**

Form -> StringGrid
     -> AsmJob -> Sam1, Sam2
StringGrid.AsmJob:= AsmJob;


**Free:**

Form -> StringGrid -> AsmJob -> Sam1, Sam2

我真的不明白我在哪里尝试双重释放或在对象被释放后覆盖它。


编辑:

我得到的一些错误:

  • FastMM 在空闲块扫描操作期间检测到错误。FastMM 检测到一个块在被释放后被修改。

  • FastMM 在空闲块扫描操作期间检测到错误。块头已损坏。

细节:

The current thread ID is 0x19C, and the stack trace (return addresses) leading to this error is: 
402E77 [System][@FreeMem] 
4068DC [System][@DynArrayClear] 
405E2D [System][@FinalizeArray] 
405D31 [System][@FinalizeRecord] 
40432F [System][TObject.CleanupInstance] 
404272 [System][TObject.FreeInstance] 
404641 [System][@ClassDestroy] 
4D313E [UnitSam.pas][TSam.Destroy][297] 
4042BF [System][TObject.Free] 
4149ED [SysUtils][FreeAndNil] 
4D9C0A [UnitAsmJob.pas][UnitAsmJob][TAsmJob.Destroy][180]  

我在 IDE 中启用了所有“调试”选项,包括“范围检查”。此外,FastMM4 设置为超级积极的调试模式。如果没有 FastMM 或在调试器之外,程序运行得很好——但我知道这并不意味着错误不再存在。实际上,它(可能)工作了一年多,直到我安装了 FastMM。


编辑:

谢谢大家。不,我觉得我正在朝着好的方向前进。

程序的结构更复杂,我只提供了主干来保持原始帖子的小。但见鬼,它已经变大了 :) 所以,这些 TSam 对象用于从磁盘加载数据。每个对象中有一个文件。他们也在做一些处理和数据验证。对于这些 TSam 中的每一个,我还有一个图形对象,它在屏幕上(以图形方式)显示 TSam 对象中包含的数据。TStringGrid 中的每一行也以文本形式显示 TSam 中的数据。

我有一个问题:如果我将程序分成小块以找出错误所在,错误还会出现吗?或者它可能只出现在这个特定的配置中?


回答“如何将 AsmJob 分配给 TStringGrid,以便 TStringGrid 破坏 AsmJob,你能告诉我们吗?”

MyGrid = TStringGrid
  public 
    AsmJob: TAsmJob; 
  end; 

然后在 TForm.Create (保存网格的表单)的某个地方,我做

MyGrid.AsmJob=AsmJob; 

在 MyGrid 的析构函数中,我这样做:

begin 
  FreeAndNil(AsmJob); 
  inherited 
end;
4

4 回答 4

14

此错误意味着您的代码损坏了内部内存管理器的结构。当 MM 检测到这一点时,您的调用堆栈代表点。这不是错误路径或与之相关的任何内容。实际错误发生在这一刻之前。它可能与提到的类相关,也可能不相关。

您应该尝试在完全调试模式下使用“范围检查错误”选项(不要忘记进行构建,而不是编译)和 FastMM(启用 CheckHeapForCorruption、CatchUseOfFreedInterfaces и DetectMMOperationsAfterUninstall 选项)

您还可以打开 FullDebugModeScanMemoryPoolBeforeEveryOperation 全局变量,以便在问题发生后几乎立即得到错误,但此选项会减慢您的执行速度。

最好的选择可能是定期调用 ScanMemoryPoolForCorruptions。在一个地方调用它。有错误吗?早点打电话。还是有错误?再早点打电话。没有错误?您的问题位于最后一次通话之间。现在您可以使用 FullDebugModeScanMemoryPoolBeforeEveryOperation 变量来获取精确位置。只需在此代码区域将其打开,然后立即将其关闭。

有一个非常相似的错误:“FastMM 检测到一个块在被释放后被修改”。在这种情况下,您的代码修改的不是内部结构,而是其他根本不使用的内存(“空闲内存”)。

顺便说一句,您的错误不是双重错误!如果这是一个双释放调用,FastMM 会明确地告诉你(很容易检测到,因为你正在尝试释放未使用或不存在的内存块):“已尝试释放/重新分配一个未分配的块”。

于 2009-07-10T08:25:33.803 回答
4

块头损坏通常意味着某些东西一直在覆盖内存,通常是通过执行某种不安全的操作。您是否在任何任务中使用原始指针或汇编代码?此外,如果您关闭了范围检查和边界检查,请尝试打开它们并重新构建。他们可以帮助解决很多此类问题。

于 2009-07-09T20:57:29.170 回答
2

代码中的某处可能存在逻辑竞争,其中一个对象在被释放时被写入。添加 NULL 检查和其他 IPC 机制(锁定列表等)以确保不是这种情况。

另一种选择可能是对代码进行子类化以向其中添加日志记录 - 并检查对象是否被顺序访问。

于 2013-03-08T19:30:14.607 回答
1

我问了几件事,因为我看不到您的代码。

给定以下代码:

procedure TForm1.FormCreate(Sender: TObject);
var
   wObjLst : TObjectList;
begin
   wObjLst := TObjectList.Create;
   try
      wObjlst.OwnsObjects := true;
      wObjlst.Add(TPersistent.Create);
      wObjlst.Add(TPersistent.Create);
   finally
      freeandnil(wObjlst);
   end;
end;

这工作没有错误。

你说

在运行时,一个表单会创建一个 TStringGrid,然后是创建这两个 SAM 对象的 AsmJob(并在每个对象中从磁盘加载一些数据)。AsmJob 也分配给网格。当表单被销毁时,Grid 通过释放 AsmJob 来处理它,从而释放 TSam 对象。这是问题所在:第一个对象被释放而没有问题,但第二个对象在调用其继承方法(在 Destroy 析构函数中)时死亡。

我的第一个问题是如何将 AsmJob 分配给 TStringGrid,以便 TStringGrid 破坏 AsmJob,你能告诉我们吗?

其次,为什么要创建 TObjectList 的后代来让它存储两个对象然后释放它们,而不是自己创建它们并让 TObjectList 销毁它们,如上所示。

要尝试的另一件事是从 fastmm.sourceforge.net下载完整的 FastMM4 包,安装它并使用 fulldebug dll 来准确追踪失败的对象。你和我都假设它是 SAM 对象之一,它可能是也可能不是。

于 2009-07-09T22:01:50.290 回答