【问题标题】:FastMM4 says "The block header has been corrupted"FastMM4 说“块头已损坏”
【发布时间】:2009-07-09 20:47:37
【问题描述】:

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

我创建了两个 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 在执行过程中检测到错误 空闲块扫描操作。快速MM 检测到一个块已被 释放后修改。

  • 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(保存 Grid 的表单)的某个地方,我会这样做

MyGrid.AsmJob=AsmJob; 

我在 MyGrid 的析构函数中:

begin 
  FreeAndNil(AsmJob); 
  inherited 
end;

【问题讨论】:

    标签: delphi delphi-7 fastmm memory-corruption


    【解决方案1】:

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

    You should try to use "Range check errors" option (don't forget to make Build, not Compile) and FastMM in full debug mode (with CheckHeapForCorruption, CatchUseOfFreedInterfaces и DetectMMOperationsAfterUninstall options enabled).

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

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

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

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

    【讨论】:

    • 谢谢亚历山大。我对“ScanMemoryPoolForCorruptions”一无所知。我猜是 FastMM DLL 提供的一个函数。我现在就去搜索一下。
    • 这是标准 FastMM4.pas 中的功能。它来自 FastMM 的完整独立版本。它在 FastMM 的版本中不存在,它集成到 Delphi 中。这里没有有问题的 DLL。这只是通常的 pas 文件中的一个功能;)
    • 遗憾的是链接已失效。但您可以通过以下方式访问它:web.archive.org/web/20091007162116/http://blog.eurekalog.com/…
    【解决方案2】:

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

    【讨论】:

    • 嗨梅森。没有 ASM,没有原始指针操作,范围检查始终开启。这正是我今天一整天都想弄清楚的:我可以在哪里覆盖那个该死的对象。
    • 我以前见过这样的情况。最终将其追踪到我正在使用的第 3 方库,该库正在使用原始指针进行一些操作并摸索其中的一些。可能是这种情况吗?
    • 顺便说一句,看起来它不是被覆盖的对象,而是内存块头。但为了确定起见,请尝试在清理代码中放置一个断点,以便在析构函数运行之前检查该对象的状态。在调试器中查看它并确保其字段有效。 (如果没有,那么您可以使用地址断点来轻松查找覆盖内存的内容。)
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      给定以下代码:

      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,然后是 AsmJob 创建这两个 SAM 对象(并在每个对象中从磁盘加载一些数据 他们)。 AsmJob 也分配给网格。当表格被破坏时, Grid 通过释放 AsmJob 来处理它,这释放了 TSam 对象。这是问题所在:第一个对象被释放而没有问题 但是第二个在它继承的方法(在 Destroy 析构函数)被调用。

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

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

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

      【讨论】:

      • 嗨瑞恩。最初,ObjectList 设置为 OwnObj=true,但现在我“手动”释放对象以查看错误出现的位置。这就是我如何确定错误出现在对 TSam 对象的继承(销毁)的调用中。反正!如果我没有手动创建和使用 TSam 对象(这意味着没有 TAsmJob - 这是这些对象的一种管理器),一切都会很好。我完全没有错误。 -------- PS:我已经在完全调试模式下的FastMM。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多