delphi - 这怎么会是内存泄漏呢?

标签 delphi memory-leaks fastmm

FastMM 将此行报告为内存泄漏的来源:

StrClassName := MidStr (curLine, length(START_OF_CLASSNAME)+1, length(curline)+1)

CopyMidStr 怎么了?这只是Delphi 2007编译器的错误,还是更高版本也有这个问题? 这是link到 FastMM 报告的副本,以及 image我的应用程序如何显示这些类型的报告。看,为了显示 VirtualTreeView 中的节点,我需要一个新的数据类型。我称之为TMemoryLeak。当我解析报告时,我给我的 TMemoryLeak 一个类名、调用堆栈、它的大小等。但是当应用程序关闭并且 FastMM 启动时,上面的复制行似乎泄漏了内存。我将调用堆栈的大小、整个对象取消分配,但作为字符串的 ClassName 字段始终会占用内存。

更新(来自评论)

这里是声明以及构造函数和解构函数。至于生命周期 - 一旦对象被用来显示节点树,对象的解构函数就会被调用,之后它们就过时了,并被释放(我希望)。

TMemoryLeak = class(TObject)
    private
      fID              :integer;
      fSize            :integer;
      fTotalSize       :integer;
      fCallStack       :TStringList;
      fClassName       :string;
      fRepeatedInstance:integer;


    public
      property ID               :integer      read fID                write fID;
      property TotalSize        :Integer      read fTotalSize         write fTotalSize;
      property Size             :integer      read fSize              write fSize;
      property CallStack        :TStringList  read fCallStack         write fCallStack;
      property ClassName        :string       read fClassName         write fClassName;
      property RepeatedInstance :integer      read fRepeatedInstance  write fRepeatedInstance;
      class function Equal(xA: TMemoryLeak; xB: TMemoryLeak): Boolean;
      procedure clear;
      constructor create;
      destructor destroy; override;
  end;

  TMemoryLeakList=class(TObjectList)
    private
      fSortType         :TMlSortType;
      fSortDirection    :TMLSortDirection;
      fTotalLeakSize    :integer;
      fClassName        :string;
      fRepeatedInstance :Integer;
      fID               :Integer;
      function  GetItem(Index: Integer): TMemoryLeak;
      procedure SetItem(Index: Integer; const Value: TMemoryLeak);

    public
      property Items[Index: Integer]:TMemoryLeak      read GetItem             write SetItem; default;
      property TotalLeakSize        :integer          read fTotalLeakSize      write fTotalLeakSize;
      property SortType             :TMLSortType      read fSortType           write fSortType;
      property SortDirection        :TMLSortDirection read fSortDirection      write fSortDirection;
      property ClassName            :string           read fClassName          write fClassName;
      property RepeatedInstance     :integer          read fRepeatedInstance   write fRepeatedInstance;
      property ID                   :Integer          read fID                 write fID;

      function Add(AObject: TMemoryLeak): Integer;
      procedure Clear();
      procedure Sort;

      constructor create;
      destructor destroy; override;
  end;


constructor TMemoryLeak.create;
  begin
    inherited;
    fCallStack := TStringList.create;
    fRepeatedInstance:=0;
  end;
  destructor TMemoryLeak.destroy;
  begin
    clear;
  end;
  procedure TMemoryLeak.clear;
  begin
    fCallStack.Clear;
  end;
  class function TMemoryLeak.Equal(xA, xB: TMemoryLeak): Boolean;
  var i:Integer;
  begin
    Result:=False;

    if xA.ClassName = xb.ClassName then
    begin
      if xA.size = xb.size then
      begin
        if xA.CallStack.Count = xB.CallStack.Count then
        begin
          for i := 0 to xa.CallStack.Count - 1 do
          begin
            if CompareStr(xA.CallStack[i], xB.CallStack[i]) <> 0 then
            begin
             break;
            end;
          end;
          if i = xa.CallStack.Count then
            Result:=True;
        end
      end
    end

  end;

  { TMemoryLeakList }

  constructor TMemoryLeakList.create;
  begin
    inherited;
    fSortType         :=stID;
    fSortDirection    :=sdAsc;
    fClassName        :='';
    fRepeatedInstance :=0;
  end;
  destructor TMemoryLeakList.destroy;
  begin
    Clear;
  end;
  procedure TMemoryLeakList.Clear;
  var i : Integer;
  begin
      for i := 0 to Count - 1 do
        Items[i].clear;
  end;

最佳答案

合理的解释是内存泄漏。

我认为您对 FastMM 泄漏报告的工作原理存在误解。您似乎从泄漏报告中推断出 CopyMidStr 等是造成泄漏的原因。事实并非如此。当分配内存但随后未释放内存时,会报告泄漏。对于像 CopyMidStr 这样的函数,它们的工作是创建新字符串,这自然涉及内存分配。报告泄漏是因为创建用于保存字符串缓冲区的内存未释放。当其余代码无法释放该内存时,这不是分配函数 CopyMidStr 的错误。

Delphi 2007 是一个成熟的产品,字符串的内存管理是正确的。也许您会进行一些手动内存复制,从而绕过字符串的引用计数。您是否通过调用 FillChar 将某些变量/字段设置为 nil ?您是否使用 FreeMem 而不是 Dispose 来处置记录?前者不会减少字符串引用计数。像这样的事情很可能是泄漏的原因。

<小时/>

查看您发布的代码摘录,这是一个问题:

destructor TMemoryLeakList.destroy;
begin
  Clear;
end;

您未能调用继承的析构函数。这意味着列表中的成员不会被销毁。这就解释了为什么你的字符串没有被破坏。

事实上,您不需要为列表类提供析构函数。只需删除它并让继承的 TObjectList 析构函数来完成工作即可。由于 OwnsObjects 默认为 True,因此列表中的任何成员一旦从列表中删除以及列表本身被销毁,就会立即被销毁。

如果您的 Clear 方法确实清除了列表,那么就会发生这种情况。但您的 Clear 并不是真正的 Clear。容器中真正的Clear 预计会删除所有成员。您应该删除 Clear 并依赖继承的版本。

TMemoryLeak中,您也无法调用继承的析构函数。并且也无法销毁该类拥有的字符串列表实例。

总而言之,我会像这样编写这些构造函数和析构函数:

constructor TMemoryLeak.Create;
begin
  inherited;
  fCallStack := TStringList.Create;
end;

destructor TMemoryLeak.Destroy;
begin
  fCallStack.Free;
  inherited;
end;

constructor TMemoryLeakList.Create;
begin
  inherited;//by default OwnsObjects is set to True, list manages member lifetime
  fSortType :=stID;
  fSortDirection :=sdAsc;
end;

然后删除析构函数,并删除Clear方法。从 TObjectList 继承的版本就足够了。

在评论中您指出:

The objects' destructors are called as soon as the objects are used to display the node tree. After that they are obsolete, and are deallocated (I hope).

我想说这很可能没有帮助。由于您在 OwnsObjects 模式下创建了对象列表,因此您根本不应该销毁该列表的成员。您已要求列表本身执行此操作。你们不能同时做到。而且“我希望”评论并没有让我对这段代码的正确性充满信心。

由于我们无法看到您的所有代码,因此我无法确定这就是它的全部问题。

最重要的是您的代码存在泄漏。相信FastMM!

关于delphi - 这怎么会是内存泄漏呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13379753/

相关文章:

c - 关于c编程的内存泄漏问题

ios - 仪器中存在内存泄漏的类

delphi - 为什么在嵌入式 Windows XP 下运行的 Delphi 2006 应用程序出现内存错误

delphi - 使用 INDY 检测远程服务器上文件的 Mime 类型

android - 如何使用Android Profiler在Android中查找不需要的引用

delphi - Delphi NOW 函数的结果如何受 "Adjust clock for daylight saving changes"选项的影响

Delphi:FastMM虚拟内存管理引用?

delphi - FastMM 是否检测所有内存泄漏

Delphi:更改链接TLinkLabel的字体

arrays - delphi 将文件作为字节数组发送到 Rest 服务