Detectando memory Leaks em delphi Win32 com CnMemProf e FastMM4

Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login
Confirmar voto
0
 (3)  (0)

nesse artigo é exemplificado de forma rápida como se evitar memory leaks, e onde eles já existem como detectá-los.

p{ line-height: 12px; } br { line-height: 12px; } .purecode { border: 2px dotted #663366; line-height: 13px; display: block; font-family: Fixedsys; font-size: 11px; color: #000099; background-color: #ddddff; width: 99%; text-align: justify; } .code { border: 2px dotted #663366; line-height: 13px; display: block; font-family: Fixedsys; font-size: 11px; color: #000099; background-color: #ddddff; width: 99%; text-align: justify; } .text { line-height: 13px; font-size: 13px; font-family: Trebuchet MS; color: #666666; background-color: #eeeeee; }     Vamos conversar sobre memory leaks.

    Quanto mais programamos Orientado aObjetos, mais temos a necessidade de instanciar componentes semcolá-los na nossa form, ou seja, instanciá-los em runtime, ou deinstanciá-los dentro denossas porprias classes, quando são membros destas.
Isso pode causar diversos memory –leaks causados por 4 fatores:
  1. esquecimento de dar umfree no objeto ou componentes sem owner que não são liberados
  2. uma exeption, abort,exit, close, halt ou coisa parecida acontecendo antes do free.
  3. Instanciar 2 vezes umobjeto em uma mesma variável, perdendo a referencia do primeiro.
  4. Ponteiros que apontampara estruturas alocadas dinamicamente que você esquece de dar umfreemem.
    A edição 72 da revistaclubedelphi fala sobre isso, e até fala sobre quando usar osowners self, application, formx ou nil.Nem sempre estamos dentrode uma form,nem sempre nossa classe é um descendente de TComponent, entãopassar self, application ou qualquer outra coisa é impossível,certo? Então temos de usar o nil.
    Um outro motivo para se usar o nil éque cada vez que você usa como owner self, outro componente ouapplication (que também é um Tcomponent), umareferencia a este objeto será adicionada a um array decomponents do owner. Imagine que cada componente, inclusiveapplication, tem um vetor de componentes. E cada vez que estecomponente vira owner de uma nova instancia de um outro componentequalquer(quando você instancia um componente com ownerdiferente de nil), um ponteiro para essa nova instancia (4 bytes, omesmo tamanho de um integer) é adicionado a esse vetor. Quandoesse owner é liberado da memória com free um contadorvai percorrendo todo esse vetor dando free para destruir cada umdesses outros componentes instanciados. Imagine o tempo e o custodisso, em memória e processador, para liberar da memóriaum cara que seja owner de 1000000 de objetos, por exemplo.
    Se quiser faça o teste: crie umaclasse pesada, tipo um form. Faça um loop de 1 a 1000000instanciando uma copia desse form passando como parâmetro ownerou o application ou uma outra form qualquer. Não precisa darshow. Independente de você liberar da memória depois ounão, veja o tempo que demora. Você pode usar para isso o gettickcount.
    Faça o mesmo teste, sóque dessa vez passando nil. Bem mais rápido certo? Isso porquequando você passa nil como owner o componente não temowner, então você economiza varia operações,como colocar uma referencia a esse objeto no vetor de componentes(sem owner ele não existe).Mas e para liberar damemória?Agora não vai liberar sozinho....Sempre que você criar umcomponente em runtime com owner nil faça da seguinte maneira:
Try
    MinhaQuery := TQuery.Create(nil);
    //operações comMinhaQuery......
finally
    MinhaQuery.Free;
End;

Ou

Try
    Try
        MinhaQuery :=TQuery.Create(nil);
       //operações comMinhaQuery......
    Except
        //tratamento daexceção
    end;
finally
    MinhaQuery.Free;
End;
  
    Isso garante que o freesempre seráexecutado e o objeto sempre liberado da memória.
Agora, como detectar memoryleaks quejá existem a um tempão no sistema por falta de atençãoe você não sabe exatamente onde?
Você pode usar o CNWizards(conhecido também como CNPack), que é um excelenteconjunto de bibliotecas e add-ins para o delphi. Ou você podeusar o FastMM4.
http://www.cnpack.org/http://sourceforge.net/projects/fastmm/

Usando oCNpack:Numa form qualquer coloqueum Button eno onclick dele digite o código abaixo:

procedureTForm1.btCriaClick(Sender:TObject);
var
    obj: TStringList;
begin
    obj := TStringList.Create;
    obj.Add('vitor');
    //tirando apenas essa linha podemoscriar um tipo de memory leak muito comum,
//que é instanciar empontos diferentes do código dois objetos 
    //numa mesma variavel eesquecer de libera-los
    //obj.Free;
    obj := TStringList.Create;
    obj.Add('rubio');
    obj.Free;
    //outro tipo comum de memory leak,criar objetos para uso imediato e esquecer de liberar
    {
        withTStringList.Create do
        begin
           //Free;
        end;
        withTButton.Create(nil) do
        begin
           Name := 'btZeh';
           //Free;
        end;
    }
end;

    Repare que o código possui3maneiras diferentes de se causar um memory leak, mas duas estãocomentadas, usaremos uma só. Fique a vontade para testar asoutras.Se você instaloucorretamente oCnPack, agora você tem alguns novos templates de projetos, e osearch path do seu delphi aponta para alguns locais onde tem algumasnovas units interessantes.Vá para a unit do seuprojeto.dpr (project1.dpr) e adicione em primeiro lugar no uses a unit cnMemProf, assim:
programProject1;
uses
CnMemProf,
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES} //depois do begin, antes doapplication.initialize configure o valor dessas 5 variaveis globais: mmPopupMsgDlg := True;
mmShowObjectInfo := True;
mmUseObjectList := True;
mmSaveToLogFile := True;
mmErrLogFile :='D:\vitor\exemplos\CnMemoryProfiler\log.log'.


    Seu dpr ficará assim:
programProject1;
uses
CnMemProf,
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES} begin
   //configuração doCnMemProf
    mmPopupMsgDlg := True;
    mmShowObjectInfo := True;
    mmUseObjectList := True;
    mmSaveToLogFile := True;
    mmErrLogFile :='D:\vitor\exemplos\CnMemoryProfiler\log.log';
    Application.Initialize;
    Application.CreateForm(TForm1,Form1);
    Application.Run;
end.
    Quanto mais informação dedebug melhor, então vá a Project options e na aba compiler marque “use debug DCUs” (desmarcando o“optimization” e marcando o “stack Frames” você temmais informações de debug, mas essas não vãoser usadas aqui)Execute a aplicação,clique no botão que cria os objetos sem destruir e feche aaplicação. Você receberá uma mensagemdizendo que ocorreram memory leaks. Veja o log:
:::::::::::::::::::::::::::::::::::::::::::::::::::::
24/11/2008 15:56:43
Application total run time: 0 hour(s) 0minute(s) 2 second(s)¡£
There are 77 allocated before replacememory manager.
HeapStatus.TotalAddrSpace: 1024 KB
HeapStatus.TotalUncommitted: 992 KB
HeapStatus.TotalCommitted: 32 KB
HeapStatus.TotalFree: 29 KB
HeapStatus.TotalAllocated: 1 KB
TotalAllocated div TotalAddrSpace: 0%
HeapStatus.FreeSmall: 0 KB
HeapStatus.FreeBig: 29 KB
HeapStatus.Unused: 0 KB
HeapStatus.Overhead: 0 KB
Objects count in memory: 3
1) 0000000000CA4A8C - 55($0037)Byte -
2) 0000000000CA4AC0 - 38($0026)Byte -
3) 0000000000CA38F0 - 23($0017)Byte –

    Certo, quem não manja muito dehexadecimal e nem de assembly, como é meu caso, percebe que háum memory leak, mas não exatamente onde. Vamos ver seconseguimos uma informação mais detalhada.
    Crie um outro projeto igual aesse,descompacte as units da biblioteca FastMM4 na pasta do projeto eedite o arquivo FastMM4Options.inc
    Mude a linha {.$define FullDebugMode}para {$define FullDebugMode} e a linha {.$define ClearLogFileOnStartup} para {$define ClearLogFileOnStartup}(para limpar o log a cada novaexecução).
    Adicione a unit FastMM4 como a primeiraunit no uses do seu project1.dpr e sete os valores das variáveis:

FullDebugModeScanMemoryPoolBeforeEveryOperation := True;
SuppressMessageBoxes:=False;

O seu dpr ficará assim:

programProject1;
uses
FastMM4,
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
begin
    //configuração doFastMM4
   FullDebugModeScanMemoryPoolBeforeEveryOperation := True;
    SuppressMessageBoxes:=False;
    Application.Initialize;
    Application.CreateForm(TForm1,Form1);
    Application.Run;
end.

Executea aplicação, crieo objeto sem destruir (clicando no botão), feche a aplicaçãoe verá que aparecerá uma mensagem de memory leak umpouco mais detalhada. Agora veja o log, atenção para aárea em negrito:

--------------------------------2008/11/2416:11:25--------------------------------
A memory block has been leaked. Thesize is: 20
This block was allocated by thread0x2C4, and the stack trace (return addresses) at the time was:
402D38[system.pas][System][@GetMem][2439]
41B68A[classes.pas][Classes][TStringList.AddObject][4589]
41B60E[classes.pas][Classes][TStringList.Add][4576]
460058[Unit1.pas][Unit1][TForm1.btCriaClick][59]
4398BC[Controls.pas][Controls][TControl.Click][4705]
4308D4[StdCtrls.pas][StdCtrls][TButton.Click][3472]
430A3B[StdCtrls.pas][StdCtrls][TButton.CNCommand][3524]
43968E[Controls.pas][Controls][TControl.WndProc][4645]
43D20B[Controls.pas][Controls][TWinControl.WndProc][6342]
430723[StdCtrls.pas][StdCtrls][TButtonControl.WndProc][3414]
439399[Controls.pas][Controls][TControl.Perform][4552]
The block is currently used for anobject of class: Unknown
The allocation number is: 504
Current memory dump of 256 bytesstarting at pointer address 7FF8F570:
01 00 00 00 05 00 00 00 76 69 74 6F 7200 EE 00 19 7E 80 80 80 80 80 80 00 00 00 00 D1 F6 F8 7F
00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 96 01 00 00 38 2D 40 00 F8 E0 41 00 EF E3 41 00
28 E3 41 00 C7 0B 42 00 AA 6B 43 00 15B1 43 00 34 58 45 00 EA F1 41 00 3C C3 41 00 AD 95 41 00
C4 02 00 00 63 2D 40 00 8B AF 43 00 8746 45 00 F0 54 45 00 89 08 42 00 03 31 45 00 76 44 40 00
D7 6F 81 7C 00 00 00 00 00 00 00 00 0000 00 00 C4 02 00 00 14 00 00 00 00 00 00 00 D4 03 2A 01
84 6C 46 00 80 80 80 80 80 80 80 80 8080 80 80 80 80 80 80 2B FC D5 FE 00 00 00 00 B1 F3 F8 7F
00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 FB 01 00 00 38 2D 40 00 8A B6 41 00 0E B6 41 00
74 00 46 00 BC 98 43 00 D4 08 43 00 3B0A 43 00 8E 96 43 00 0B D2 43 00 23 07 43 00 99 93 43 00
. . . . . . . . v i t o r . î . . ~ € € € € € € . . . . Ñ ö ø 
. . . . . . . . . . . . . . . . – . . . 8 - @ . ø à A . ï ã A .
( ã A . Ç . B . ª k C . . ± C . 4 X E . ê ñ A . < Ã A . ­ • A .
Ä . . . c - @ . ‹ ¯ C . ‡ F E . ð T E . ‰ . B . . 1 E . v D @ .
× o � | . . . . . . . . . . . . Ä . . . . . . . . . . . Ô . * .
„ l F . € € € € € € € € € € € € € € € € + ü Õ þ . . . . ± ó ø 
. . . . . . . . . . . . . . . . û . . . 8 - @ . Š ¶ A . . ¶ A .
t . F . ¼ ˜ C . Ô . C . ; . C . Ž – C . . Ò C . # . C . ™ “ C .
--------------------------------2008/11/2416:11:25--------------------------------
A memory block has been leaked. Thesize is: 36
This block was allocated by thread0x2C4, and the stack trace (return addresses) at the time was:
402DBD[system.pas][System][@ReallocMem][2550]
41BA7D[classes.pas][Classes][TStringList.Grow][4705]
41BB90[classes.pas][Classes][TStringList.InsertItem][4730]
41B68A[classes.pas][Classes][TStringList.AddObject][4589]
41B60E[classes.pas][Classes][TStringList.Add][4576]
460058[Unit1.pas][Unit1][TForm1.btCriaClick][59]
4398BC[Controls.pas][Controls][TControl.Click][4705]
4308D4[StdCtrls.pas][StdCtrls][TButton.Click][3472]
430A3B[StdCtrls.pas][StdCtrls][TButton.CNCommand][3524]
43968E[Controls.pas][Controls][TControl.WndProc][4645]
43D20B[Controls.pas][Controls][TWinControl.WndProc][6342]
The block is currently used for anobject of class: Unknown
The allocation number is: 503
Current memory dump of 256 bytesstarting at pointer address 7FF95880:
78 F5 F8 7F 00 00 00 00 80 80 80 80 8080 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
E4 8D 55 FC 80 80 80 80 00 00 00 00 6159 F9 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
B1 01 00 00 38 2D 40 00 C1 66 43 00 B8AD 43 00 C0 05 43 00 4C 08 43 00 04 DF 41 00 39 E1 41 00
EF E3 41 00 28 E3 41 00 C7 0B 42 00 AA6B 43 00 C4 02 00 00 63 2D 40 00 DF 37 40 00 D2 AF 43 00
8B AF 43 00 87 46 45 00 F0 54 45 00 8908 42 00 03 31 45 00 76 44 40 00 D7 6F 81 7C 00 00 00 00
C4 02 00 00 24 00 00 00 F8 26 42 00 7623 F1 01 84 6C 46 00 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 8080 80 80 80 80 80 80 89 DC 0E FE 00 00 00 00 11 5A F9 7F
00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 B2 01 00 00 38 2D 40 00 F5 66 43 00 B8 AD 43 00
x õ ø  . . . . € € € € € € € € € € € € € € € € € € € € € € € €
ä � U ü € € € € . . . . a Y ù . . . . . . . . . . . . . . . .
± . . . 8 - @ . Á f C . ¸ ­ C . À . C . L . C . . ß A . 9 á A .
ï ã A . ( ã A . Ç . B . ª k C . Ä . . . c - @ . ß 7 @ . Ò ¯ C .
‹ ¯ C . ‡ F E . ð T E . ‰ . B . . 1 E . v D @ . × o � | . . . .
Ä . . . $ . . . ø & B . v # ñ . „ l F . € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € ‰ Ü . þ . . . . . Z ù 
. . . . . . . . . . . . . . . . ² . . . 8 - @ . õ f C . ¸ ­ C .
--------------------------------2008/11/2416:11:25--------------------------------
A memory block has been leaked. Thesize is: 52
This block was allocated by thread0x2C4, and the stack trace (return addresses) at the time was:
402D38[system.pas][System][@GetMem][2439]
4398BC[Controls.pas][Controls][TControl.Click][4705]
4308D4[StdCtrls.pas][StdCtrls][TButton.Click][3472]
430A3B[StdCtrls.pas][StdCtrls][TButton.CNCommand][3524]
43968E[Controls.pas][Controls][TControl.WndProc][4645]
43D20B[Controls.pas][Controls][TWinControl.WndProc][6342]
430723[StdCtrls.pas][StdCtrls][TButtonControl.WndProc][3414]
439399[Controls.pas][Controls][TControl.Perform][4552]
43D3CD[Controls.pas][Controls][DoControlMsg][6388]
43DBAA[Controls.pas][Controls][TWinControl.WMCommand][6574]
458F35[Forms.pas][Forms][TCustomForm.WMCommand][4116]
The block is currently used for anobject of class: TStringList
The allocation number is: 502
Current memory dump of 256 bytesstarting at pointer address 7FF99870:
FC 76 41 00 00 00 00 00 00 00 00 00 0000 00 00 80 58 F9 7F 01 00 00 00 04 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 EB 89 4A 7A 80 80 80 80 00 00 00 00 01 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 F9 01 00 00 38 2D 40 00 BC 98 43 00 D4 08 43 00
3B 0A 43 00 8E 96 43 00 0B D2 43 00 2307 43 00 99 93 43 00 CD D3 43 00 AA DB 43 00 35 8F 45 00
C4 02 00 00 63 2D 40 00 DF 37 40 00 BC98 43 00 D4 08 43 00 3B 0A 43 00 8E 96 43 00 0B D2 43 00
23 07 43 00 99 93 43 00 CD D3 43 00 AADB 43 00 C4 02 00 00 30 00 00 00 FC 76 41 00 3A F6 FF 85
84 6C 46 00 80 80 80 80 80 80 80 80 8080 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 8080 80 80 C5 09 00 7A 80 80 80 80 00 00 00 00 00 00 00 00
ü v A . . . . . . . . . . . . . € X ù  . . . . . . . . . . . .
. . . . . . . . . . . . . . . . ë ‰ J z € € € € . . . . . . . .
. . . . . . . . . . . . . . . . ù . . . 8 - @ . ¼ ˜ C . Ô . C .
; . C . Ž – C . . Ò C . # . C . ™ “ C . Í Ó C . ª Û C . 5 � E .
Ä . . . c - @ . ß 7 @ . ¼ ˜ C . Ô . C . ; . C . Ž – C . . Ò C .
# . C . ™ “ C . Í Ó C . ª Û C . Ä . . . 0 . . . ü v A . : ö ÿ …
„ l F . € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € Å . . z € € € € . . . . . . . .
--------------------------------2008/11/2416:11:25--------------------------------
This application has leaked memory. Thesmall block leaks are (excluding expected leaks registered bypointer):
13 - 20 bytes: AnsiString x 1
21 - 36 bytes: Unknown x 1
37 - 52 bytes: TStringList x 1
Note: Memory leak detail is logged to atext file in the same folder as this application. To disable thismemory leak check, undefine "EnableMemoryLeakReporting".

    No caso, no meu projeto o memory leakficou na unit1, form1 linha 59. Se eu olhar a linha 59 da unit1:obj.Add('vitor');
    Ou seja, é uma linha muitopróxima da onde está acontecendo o memory leak: éum pouco depois da criação do primeiro objeto e umpouco antes da criação do segundo, onde eu crio porcima da mesma variável perdendo a referencia ao meu primeiroobjeto, que fica órfão na memória.
    Com um pouco de atenção podemos resolver quase todos os problemas olhando nesse log, a menosque sejam problemas de bibliotecas de terceiros as quais desconhecemos.
    Espero que tenha ajudadotodos adetonar seus memory leaks. Quem tiver uma sugestão ou souberde algum recurso ou melhor uso do CnMemory Profiler ou do FastMM4 porfavor poste aqui para um intercâmbio de conhecimento.

 
Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Receba nossas novidades
Ficou com alguma dúvida?