捐赠 | 广告 | 注册 | 发布 | 上传 | 关于我们    
  沪ICP备05001939号 DELPHI盒子 | 盒子论坛 | 盒子文章 | 盒子问答悬赏 | 最新更新 | 论坛检索 | 下载中心 | 高级搜索    
  精品专区 | 繁體中文 | 奖励公告栏 | 直通车账号登陆 | 关闭GOOGLE广告 | 临时留言    
盒子资源分类
全部展开 - 全部合拢
检查Delphi程序内存泄露
关键字:CheckMem 内存 泄露
来 自:转载,http://www.cnblogs.com/abchjb/articles/18685.html#Post
平 台:Win9x,Win2k/XP/NT,Win2003 下载所需:0 火柴
深浅度:初级 完成时间:2006/8/7
发布者:wqyfavor 发布时间:2006/8/7
编辑器:DELPHI7 语  种:简体中文
分 类:安全 下载浏览:0/25300
加入到我的收藏
下载错误报错
登陆以后才能下载
 用户名:
 密 码:
自动登陆(30天有效)
无图片
盒子的内容是挺多的,可查来查去竟然没发现CheckMem的信息。

特此转载,原出处:http://www.cnblogs.com/abchjb/articles/18685.html#Post

一、使用步骤:

A)、将CheckMem.pas单元加入到工程中

B)、修改工程文件,将'CheckMem.pas'放到uses下的第一句

program Project1;

uses
  CheckMem in 'CheckMem.pas',
  Forms,
  Unit1 in 'Unit1.pas' {Form1} ;//其他单元文件
{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

C)、正常的编译、运行应用程序

D)、退出应用程序后,将在应用程序目录下生成报告(如果有漏洞的话,如果没有则不生成)。

二、报告解读:

报告的内容:

===== Project1.exe,2004-6-11 15:36:55 =====

     可用地址空间 : 1024 KB(1048576 Byte)
       未提交部分 : 1008 KB(1032192 Byte)
       已提交部分 : 16 KB(16384 Byte)
         空闲部分 : 13 KB(14020 Byte)
       已分配部分 : 1 KB(2024 Byte)
 全部小空闲内存块 : 0 KB(232 Byte)
 全部大空闲内存块 : 11 KB(11864 Byte)
   其它未用内存块 : 1 KB(1924 Byte)
   内存管理器消耗 : 0 KB(340 Byte)
     地址空间载入 : 0%

当前出现 3 处内存漏洞 :
   0) 0000000000F33798 -   19($0013)字节 - 不是对象
   1) 0000000000F337A8 -   18($0012)字节 - 不是对象
   2) 0000000000F337B8 -   18($0012)字节 - 不是对象

解读如下:

当前出现 3 处内存漏洞 :(有三个内存块分配了,但未释放。注意这里不是指对象变量或指针变量的地址,是对象的内存区域或指针指向的内存地址)

序号 未释放内存的地址  内存大小    是否是对象?如果是列出对象的Name及class并指出对象实现的单元文件名

0) 0000000000F33798 -   19($0013)字节 - 不是对象

三、测试例子:

测试用的代码:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    aa:TstringList;
    bb:tbutton;
  end;

var
  Form1: TForm1;
  def:pointer;
implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

begin
   aa:=TstringList.Create;
   bb:=Tbutton.Create(nil);
   aa.Add('abcdefdafasdfasdfasdfasdf');
   application.MessageBox(pchar(aa.Strings[0]),'asdf',MB_OK);
//   aa.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  p:Pointer;
begin
   GetMem(def,10);
   p:=def;
   fillchar(p,10,$65);
   application.MessageBox (def,'aaa',MB_OK);
//   freemem(def,10);
end;

end.

我们先点击button1,然后退出。出现的报告如下:

当前出现 10 处内存漏洞 :
   0) 0000000000F3109C -   67($0043)字节 - 不是对象
   1) 0000000000F316A4 -   39($0027)字节 - 不是对象
   2) 0000000000F33798 -   55($0037)字节 - (未命名): TStringList (48 字节) - In Classes.pas
   3) 0000000000F337CC -  518($0206)字节 -  : TButton (512 字节) - In StdCtrls.pas
   4) 0000000000F339D0 -   42($002A)字节 - MS Sans Serif : TFont (36 字节) - In Graphics.pas
   5) 0000000000F339F8 -   38($0026)字节 - (未命名): TSizeConstraints (32 字节) - In Controls.pas
   6) 0000000000F33A1C -   30($001E)字节 - (未命名): TBrush (24 字节) - In Graphics.pas
   7) 0000000000F33A38 -   38($0026)字节 - 不是对象
   8) 0000000000F33A5C -   38($0026)字节 - 不是对象
   9) 0000000000F33A80 -   42($002A)字节 - 不是对象

把bb:=Tbutton.Create(nil);注释掉://bb:=Tbutton.Create(nil);再重新编译,然后运行,再点button1。出现的报告如下:

当前出现 3 处内存漏洞 :
   0) 0000000000F33798 -   55($0037)字节 - (未命名): TStringList (48 字节) - In Classes.pas
   1) 0000000000F337CC -   38($0026)字节 - 不是对象
   2) 0000000000F337F0 -   42($002A)字节 - 不是对象

说明了:一个对象未释放,将引起多处内存泄漏(因为一个对象可能包含多个子对象)

OK,我们再来测试button2(注意,这次我们不点击button1,只点击button2 一次),产生的报告如下:

当前出现 1 处内存漏洞 :
   0) 0000000000F33798 -   19($0013)字节 - 不是对象

再来一次,这次点击button2 三次:

当前出现 3 处内存漏洞 :
   0) 0000000000F33798 -   19($0013)字节 - 不是对象
   1) 0000000000F337A8 -   18($0012)字节 - 不是对象
   2) 0000000000F337B8 -   18($0012)字节 - 不是对象

这说明:对于每一个未释放的内存,CheckMem都将记录下来!再注意上面的未释放内存的地址是紧挨着的,因此如果看到这样的报告,可以猜想为一变量,多次分配,但未释放!

四、内存泄漏测试及修复的技巧:(翻译自MemProof帮助的部分内容,翻译得不好,请大家来信指导)

The following are a couple of tips that can be usefull when fixing leaks  in an application :

下面的这些技巧对于修复应用程序的内存泄漏非常有用:

* First just launch the app and then close it. If even this action generates leaks, fix those leaks first. Only after the main leaks are fixed, you should go into specific functionality areas of the application.

*首先,运行应用程序然后马上退出。如果这样操作也产生内存泄漏,先修复这些漏洞。只有先修复这些主要的泄漏,你才能进行特定功能的测试。

* In your Delphi/C++Builder project options, remove as much forms as possible from the Auto-Create option. Create your forms dynamically.

*在你的delphi/C++Builder工程选项中,尽可能地不要使用自动创建窗体,你需要时再动态创建。


* 注意在循环中创建或分配的内存的代码。如果它们未释放,可能引起大量的内存泄漏。

* Go for the biggest classes first - if you see a TMyFom <class> leaking, try to fix this one first instead of going after a tiny TFont class. Since a Form will usually contain a lot of other classes, with one shot you will have fixed a lot of contained leaks.

*先修复大的类,比如你看到TMyFom 类有泄漏,先解决它的问题,然后再解决像TFont 这样的小类。一个form类经常包含多个子类。修复一个form的未释放问题,你将解决大量该form包含的子对象未释的问题。

* Go for the easy fixes first. Some leaks fixes are very easy and obvious - if you fix the easy ones first, you will keep them out of your way.

*首先修复容易修复的漏洞。一些泄漏是非常容易被发现的,如果你先修复他们,你就不用老想着他们了。

 

附:CheckMem.pas单元

unit CheckMem; file://Add it to the first line of project uses

interface

procedure SnapCurrMemStatToFile(Filename: string);

implementation

uses
  Windows, SysUtils, TypInfo;

const
  MaxCount = High(Word);

var
  OldMemMgr: TMemoryManager;
  ObjList: array[0..MaxCount] of Pointer;
  FreeInList: Integer = 0;
  GetMemCount: Integer = 0;
  FreeMemCount: Integer = 0;
  ReallocMemCount: Integer = 0;

procedure AddToList(P: Pointer);
begin
  if FreeInList > High(ObjList) then
  begin
    MessageBox(0, '内存管理监视器指针列表溢出,请增大列表项数!', '内存管理监视器', mb_ok);
    Exit;
  end;
  ObjList[FreeInList] := P;
  Inc(FreeInList);
end;

procedure RemoveFromList(P: Pointer);
var
  I: Integer;
begin
  for I := 0 to FreeInList - 1 do
    if ObjList[I] = P then
    begin
      Dec(FreeInList);
      Move(ObjList[I + 1], ObjList[I], (FreeInList - I) * SizeOf(Pointer));
      Exit;
    end;
end;

procedure SnapCurrMemStatToFile(Filename: string);
const
  FIELD_WIDTH = 20;
var
  OutFile: TextFile;
  I, CurrFree, BlockSize: Integer;
  HeapStatus: THeapStatus;
  Item: TObject;
  ptd: PTypeData;
  ppi: PPropInfo;

  procedure Output(Text: string; Value: integer);
  begin
    Writeln(OutFile, Text: FIELD_WIDTH, Value div 1024, ' KB(', Value, ' Byte)');
  end;

begin
  AssignFile(OutFile, Filename);
  try
    if FileExists(Filename) then
    begin
      Append(OutFile);
      Writeln(OutFile);
    end
    else
      Rewrite(OutFile);
    CurrFree := FreeInList;
    HeapStatus := GetHeapStatus; { 局部堆状态 }
    with HeapStatus do
    begin
      Writeln(OutFile, '===== ', ExtractFileName(ParamStr(0)), ',', DateTimeToStr(Now), ' =====');
      Writeln(OutFile);
      Output('可用地址空间 : ', TotalAddrSpace);
      Output('未提交部分 : ', TotalUncommitted);
      Output('已提交部分 : ', TotalCommitted);
      Output('空闲部分 : ', TotalFree);
      Output('已分配部分 : ', TotalAllocated);
      Output('全部小空闲内存块 : ', FreeSmall);
      Output('全部大空闲内存块 : ', FreeBig);
      Output('其它未用内存块 : ', Unused);
      Output('内存管理器消耗 : ', Overhead);
      Writeln(OutFile, '地址空间载入 : ': FIELD_WIDTH, TotalAllocated div (TotalAddrSpace div 100), '%');
    end;
    Writeln(OutFile);
    Writeln(OutFile, Format('当前出现 %d 处内存漏洞 :', [GetMemCount - FreeMemCount]));
    for I := 0 to CurrFree - 1 do
    begin
      Write(OutFile, I: 4, ') ', IntToHex(Cardinal(ObjList[I]), 16), ' - ');
      BlockSize := PDWORD(DWORD(ObjList[I]) - 4)^;
      Write(OutFile, BlockSize: 4, '($' + IntToHex(BlockSize, 4) + ')字节', ' - ');
      try
        Item := TObject(ObjList[I]);
        if PTypeInfo(Item.ClassInfo).Kind <> tkClass then { type info technique }
          write(OutFile, '不是对象')
        else
        begin
          ptd := GetTypeData(PTypeInfo(Item.ClassInfo));
          ppi := GetPropInfo(PTypeInfo(Item.ClassInfo), 'Name'); { 如果是TComponent }
          if ppi <> nil then
          begin
          write(OutFile, GetStrProp(Item, ppi));
          write(OutFile, ' : ');
          end
          else
          write(OutFile, '(未命名): ');
          Write(OutFile, Item.ClassName, ' (', ptd.ClassType.InstanceSize,
          ' 字节) - In ', ptd.UnitName, '.pas');
        end
      except
        on Exception do
          write(OutFile, '不是对象');
      end;
      writeln(OutFile);
    end;
  finally
    CloseFile(OutFile);
  end;
end;

function NewGetMem(Size: Integer): Pointer;
begin
  Inc(GetMemCount);
  Result := OldMemMgr.GetMem(Size);
  AddToList(Result);
end;

function NewFreeMem(P: Pointer): Integer;
begin
  Inc(FreeMemCount);
  Result := OldMemMgr.FreeMem(P);
  RemoveFromList(P);
end;

function NewReallocMem(P: Pointer; Size: Integer): Pointer;
begin
  Inc(ReallocMemCount);
  Result := OldMemMgr.ReallocMem(P, Size);
  RemoveFromList(P);
  AddToList(Result);
end;

const
  NewMemMgr: TMemoryManager = (
    GetMem: NewGetMem;
    FreeMem: NewFreeMem;
    ReallocMem: NewReallocMem);

initialization
  GetMemoryManager(OldMemMgr);
  SetMemoryManager(NewMemMgr);

finalization
  SetMemoryManager(OldMemMgr);
  if (GetMemCount - FreeMemCount) <> 0 then
    SnapCurrMemStatToFile(ExtractFileDir(ParamStr(0)) + '\CheckMemory.Log');
end.
Google
 
本站原创作品,未经作者许可,严禁任何方式转载;转载作品,如果侵犯了您的权益,请联系我们
龙脉加密锁 15元起 Grid++Report 报表 申请支付@网
 相关文章
没有相关文章
相关评论
共有评论13条 当前显示最后6条评论
qsmile 2006/8/11 11:46:18
是呀,太麻烦了一点.

用 FastMM 就不错. 还有快速内存处理的功能.
victorwoo 2006/8/11 11:55:29
FastMM一直没用成功-_-
shawn_liu 2006/8/31 11:12:24
borland的DB包,好像有泄漏。继续追下去,发现DB包引用了SqlTimSt或FMTBcd导致泄漏。
dery 2007/2/4 9:15:41
Delphi有内存检测的,只是你不知道而已。
OnCreate里加入这个就能在退出时报告:
  //报告内存泄漏
  ReportMemoryLeaksOnShutdown := True;
victorwoo 2007/2/4 13:51:53
是不是要装FastMM之类的?
我在D7,新建一个工程,双击Form,在OnCreate()里加入ReportMemoryLeaksOnShutdown := True;
报错。
[Error] Unit1.pas(27): Undeclared identifier: 'ReportMemoryLeaksOnShutdown'
不知道您这个函数是什么单元里的?
dery 2007/2/6 8:51:31
那可能是Delphi2005之后才有的吧,在System单元里:
var

  AllocMemCount: Integer deprecated; {Unsupported}
  AllocMemSize: Integer deprecated; {Unsupported}

{Set this variable to true to report memory leaks on shutdown. This setting
 has no effect if this module is sharing a memory manager owned by another
 module.}
  ReportMemoryLeaksOnShutdown: Boolean;

Delphi2006加了很多东西的!
我要发表评论 查看全部评论
 
  DELPHI盒子版权所有 1999-2023 V4.01 粤ICP备10103342号-1 更新RSS列表