• Delphi5和Delphi7属性编辑器内存泄漏问题的解决


    Delphi5和Delphi7属性编辑器内存泄漏问题的解决

     

    问题:

    Delphi5 测试一个项目时用到了 DsgnIntf.pas 单元。运行后提示内存泄漏。查看项目代码,没有发现泄漏点。

    FastMM4 显示的内存泄漏报告提示与 DsgnIntf.pas 单元有关,新建一个测试项目,引用 DsgnIntf.pas 单元,编译运行,没有提示内存泄漏。

    initialization 初始化段注册几个属性编辑器,编译运行,果然提示内存泄漏,详细报告内容如下:

     

    该内存块分配于线程 0xB10, 当时的堆栈跟踪(返回地址):

    402962 [System][@GetMem]

    40477E [System][@New]

    446105 [DsgnIntf.pas][DsgnIntf][RegisterPropertyEditor][3295]

    446364 [testMemory.pas][testMemory][RegPropEdit][27]

    4463F1 [testMemory.pas][testMemory][initialization][37]

    40397C [System][InitUnits]

    4039D2 [System][@StartExe]

    40648D [SysInit][@InitExe]

    446594 [E:Delphizproplst estMem.dpr][testMem][initialization]

    76D8336A [BaseThreadInitThunk]

    776A9902 [RtlInitializeExceptionChain]

    查看 RegisterPropertyEditor 函数,关键代码为一个指针申请了内存,加入到 ComponentClassList 中,ComponentClassList 是一个 TList,在单元的 finalization 终止化段释放了,但没有相关代码释放每个内部项的内存。同样 PropertyMapperList 也存在这样的问题。

     

    解决方法:

    复制 DsgnIntf.pas 单元到测试项目的目录中,在该单元最后加入两个函数。

    procedure DisposePropertyClassList;

    var

      i: Integer;

    begin

      if PropertyClassList = nil then

        Exit;

        for i := 0 to PropertyClassList.Count - 1 do

        Dispose(PropertyClassList[i]);

      end;

     

    procedure DisposePropertyMapperList;

    var

      i: Integer;

    begin

      if PropertyMapperList = nil then

        Exit;

        for i := 0 to PropertyMapperList.Count - 1 do

        Dispose(PropertyMapperList[i]);

      end;

     

    finalization 终止化段开始插入这两个函数。最终 finalization 终止化段如下:

     

    finalization

      DisposePropertyClassList; //uu,添加,原来只是释放了list,没有释放内部项的内存

      DisposePropertyMapperList; //uu,添加,原来只是释放了list,没有释放内部项的内存

      FreeAndNil(EditorGroupList);

      FreeAndNil(PropertyClassList);

      FreeAndNil(ComponentClassList);

      FreeAndNil(PropertyMapperList);

      FreeAndNil(InternalPropertyCategoryList);

     

    再次编译运行测试项目,没有发现内存泄漏。看来手工分配内存一定要注意释放,连专业的程序员也难免失误。发布这段内容,希望能帮助需要使用 DsgnIntf.pas 单元的开发者。

     

    后记:

    再把项目用 Delphi7 编译试试,结果也发现内存泄漏。Delphi7 使用了不同的方法注册属性编辑器,但仍然用了几个List记录相关信息,相关代码移到了 DesignEditors.pas 单元。这个单元定义了一个函数 procedure FreeEditorGroup(Group: Integer); 。这个函数释放了这几个List的内部项。可是下了断点后,没有地方运行这个函数。看来一下函数的内容,竟然发现了两个严重问题,1、没有判断List是否初始化就使用List2、没有释放List本身。

    复制 DesignEditors.pas 单元到测试项目的目录中,在该单元最后加入一个函数。

     

    procedure FreeList;

    var

      I: Integer;

      P: PPropertyClassRec;

      C: PComponentClassRec;

      M: PPropertyMapperRec;

      SelectionDef: TSelectionEditorDefinition;

    begin

      // Release all property editors associated with the group

      if PropertyClassList <> nil then

        begin

          I := PropertyClassList.Count - 1;

          while I > -1 do

          begin

            P := PropertyClassList[I];

            // if P.Group = Group then

            begin

              PropertyClassList.Delete(I);

              Dispose(P);

            end;

            Dec(I);

          end;

          FreeAndNil(PropertyClassList);

        end;

        // Release all component editors associated with the group

        if ComponentClassList <> nil then

          begin

            I := ComponentClassList.Count - 1;

            while I > -1 do

            begin

              C := ComponentClassList[I];

              // if C.Group = Group then

              begin

                ComponentClassList.Delete(I);

                Dispose(C);

              end;

              Dec(I);

            end;

            FreeAndNil(ComponentClassList);

          end;

          // Release all property mappers associated with the group

          if PropertyMapperList <> nil then

            begin

              for I := PropertyMapperList.Count - 1 downto 0 do

              begin

                M := PropertyMapperList[I];

                // if M.Group = Group then

                begin

                  PropertyMapperList.Delete(I);

                  Dispose(M);

                end;

              end;

              FreeAndNil(PropertyMapperList);

            end;

            // Release all selection editors associated with the group

            if SelectionEditorDefinitionList <> nil then//uu,TObjectList,应该自己能释放内部对象

              begin

                for I := SelectionEditorDefinitionList.Count - 1 downto 0 do

                begin

                  // SelectionDef := SelectionEditorDefinitionList[I];

                  // if SelectionDef.FGroup = Group then

                  SelectionEditorDefinitionList.Delete(I);

                end;

                FreeAndNil(SelectionEditorDefinitionList);

              end;

              // Notify everyone else that have similar registration lists that the group

              // is being unloaded.

              if Assigned(GroupNotifyList) then

                // for I := GroupNotifyList.Count - 1 downto 0 do

                // TGroupChangeProc(GroupNotifyList[I])(Group);

                FreeAndNil(GroupNotifyList); //内部对象不是在这里创建的,不用释放,释放自己就可以

                // Free the group ID for use by another group

                // if (Group >= 0) and (Group < EditorGroupList.Size) then

                // EditorGroupList[Group] := False;

                if EditorGroupList <> nil then

                  FreeAndNil(EditorGroupList);

                end;

     

    finalization 终止化段开始插入这个函数,其他内容不变。编译。。。提示 CreateResFmt 函数没有相应的重载形式!看了一下该函数的定义,应该可以被调用,检查工程选项,发现 Compliler 页的 Typed @ operator 打开了。关闭这个选项,再编译。。。运行。。。关闭。没有发现内存泄漏。

     

    另外需要注意一点:Delphi7 使用了接口技术调用属性编辑器,Delphi5 的项目转 Delphi7 除了要把一些对象参数改为接口参数外,还要及时清理接口的引用。

  • 相关阅读:
    click事件——背景高亮
    color——RGB转16进制
    导航栏高亮设置
    layui table 渲染完成后,怎样拿到表个里的所有数据
    解决 AttributeError: 'dict' object has no attribute 'has_key' 错误的方法
    2019年总结:醒悟还为时不晚
    Worker Services读取配置后,发布Windows出现的问题及解决
    C# 读取配置(详细操作,让我们一起共同成长)
    程序不包含适合于入口点的静态“Main”方法
    .NET Core3.0-Worker Services
  • 原文地址:https://www.cnblogs.com/unjiang/p/12456827.html
Copyright © 2020-2023  润新知