Recent

Author Topic: [SOLVED]How to find source of memory leak  (Read 2212 times)

rnfpc

  • Full Member
  • ***
  • Posts: 118
[SOLVED]How to find source of memory leak
« on: September 20, 2025, 07:22:41 pm »
I have a small Lazarus GUI application which uses TChart with TLineSeries. Using 'grep' utility on source code  files, following objects are created in one unit of the application:
Code: Pascal  [Select][+][-]
  1. uabg.pas: Series := TLineSeries.Create(Chart1);
  2. uabg.pas: series_hz := TLineseries.Create(Chart1);
  3. uabg.pas: series_hz2 := TLineseries.Create(Chart1);
  4. uabg.pas: series_vt := TLineseries.Create(Chart1);
  5. uabg.pas: series_vt2 := TLineseries.Create(Chart1);
  6. uabg.pas: SeriesMain := TLineSeries.Create(Chart1);
  7. uabg.pas: TextMemo := TMemo.Create(abgform);
  8. uabg.pas: sl := TStringList.Create;
  9.  
And the same objects are freed at time of exit from the program (from main form) (except sl, which is local to a procedure in the unit uabg and is freed in finally part of try-finally code in the procedure):
Code: Pascal  [Select][+][-]
  1. uabg.pas:        sl.free;
  2. umaincalc.pas:    abgform.seriesMain.free;
  3. umaincalc.pas:    abgform.series.free;
  4. umaincalc.pas:    abgform.series_hz.free;
  5. umaincalc.pas:    abgform.series_vt.free;
  6. umaincalc.pas:    abgform.series_hz2.free;
  7. umaincalc.pas:    abgform.series_vt2.free;
  8. umaincalc.pas:    abgform.TextMemo.free;
  9.  
However, the valgrind output shows still some memory is being leaked:
Code: Pascal  [Select][+][-]
  1. $ valgrind -s ./mycalc_prog
  2. ==8980== Memcheck, a memory error detector
  3. ==8980== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
  4. ==8980== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
  5. ==8980== Command: ./mycalc_prog
  6. ==8980==
  7. ==8980==
  8. ==8980== HEAP SUMMARY:
  9. ==8980==     in use at exit: 3,514,086 bytes in 47,800 blocks
  10. ==8980==   total heap usage: 475,339 allocs, 427,539 frees, 29,116,805 bytes allocated
  11. ==8980==
  12. ==8980== LEAK SUMMARY:
  13. ==8980==    definitely lost: 15,792 bytes in 48 blocks
  14. ==8980==    indirectly lost: 50,222 bytes in 2,022 blocks
  15. ==8980==      possibly lost: 6,023 bytes in 37 blocks
  16. ==8980==    still reachable: 3,306,793 bytes in 44,584 blocks
  17. ==8980==         suppressed: 0 bytes in 0 blocks
  18. ==8980== Rerun with --leak-check=full to see details of leaked memory
  19. ==8980==
  20. ==8980== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
  21.  
I have 2 questions:
Q1.  How severe is the problem of memory leak here? Can it be ignored?
Q2.  How to find where in the code the memory leak is occurring? How can valgrind or any other program help? Especially, how can Lazarus itself help in finding the source of the memory leak.

Thanks for your insight.
« Last Edit: September 24, 2025, 10:27:55 am by rnfpc »

Bart

  • Hero Member
  • *****
  • Posts: 5663
    • Bart en Mariska's Webstek
Re: How to find source of memory leak
« Reply #1 on: September 20, 2025, 07:50:58 pm »
What does heaptrace say (compile with -gh)?

Bart

Thaddy

  • Hero Member
  • *****
  • Posts: 18676
  • Jungle wars. And failing health it seems.
Re: How to find source of memory leak
« Reply #2 on: September 20, 2025, 08:06:46 pm »
compile with -gl AND -gh
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

rnfpc

  • Full Member
  • ***
  • Posts: 118
Re: How to find source of memory leak
« Reply #3 on: September 21, 2025, 03:32:48 am »
I tried compiling with -gh and -gl options. Now on running the program it stops at following  code with an exception.
Code: Pascal  [Select][+][-]
  1.  
  2. try
  3.    pH := strtofloat(edit4.text);
  4. except
  5.    pH := -1
  6. end;
  7.  
If edit4 is empty, the error is as follows:
Code: Pascal  [Select][+][-]
  1. Project raised exception class 'EConvertError' with message
  2. "" is an invalid float
  3. At address 457DAA
  4.  
Earlier there was no error if the edit text field was empty.
Now also the error disappears, and program runs well, if I remove -gh and -gl options.
How can this be corrected?
« Last Edit: September 21, 2025, 03:46:08 am by rnfpc »

jamie

  • Hero Member
  • *****
  • Posts: 7488
Re: How to find source of memory leak
« Reply #4 on: September 21, 2025, 03:43:46 am »
you could first check the field?

But in any case, that will most likely work if you don't run it under the debugger, otherwise you will see it popup.

You could also try using the TryStrToFloat(input, theoutVariable):Boolean.


etc.
The only true wisdom is knowing you know nothing

rnfpc

  • Full Member
  • ***
  • Posts: 118
Re: How to find source of memory leak
« Reply #5 on: September 21, 2025, 04:33:59 am »
TryStrToFloat certainly seems better.  It assigns 0 to float variable if edit text  field is empty. The program runs fully without any error or warnings even with -gh and -gl options. However, the memory leak problem is still persisting: 
Code: Pascal  [Select][+][-]
  1. $ valgrind -s ./mycalc_prog
  2. ==6570== Memcheck, a memory error detector
  3. ==6570== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
  4. ==6570== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
  5. ==6570== Command: ./mycalc_prog
  6. ==6570==
  7. Heap dump by heaptrc unit of .../mycalc_prog
  8. 12446 memory blocks allocated : 2541877/2560584
  9. 12446 memory blocks freed     : 2541877/2560584
  10. 0 unfreed memory blocks : 0
  11. True heap size : 983040
  12. True free heap : 983040
  13. ==6570==
  14. ==6570== HEAP SUMMARY:
  15. ==6570==     in use at exit: 3,531,814 bytes in 47,828 blocks
  16. ==6570==   total heap usage: 329,317 allocs, 281,489 frees, 24,047,466 bytes allocated
  17. ==6570==
  18. ==6570== LEAK SUMMARY:
  19. ==6570==    definitely lost: 14,894 bytes in 47 blocks
  20. ==6570==    indirectly lost: 48,387 bytes in 1,963 blocks
  21. ==6570==      possibly lost: 5,999 bytes in 36 blocks
  22. ==6570==    still reachable: 3,327,534 bytes in 44,675 blocks
  23. ==6570==         suppressed: 0 bytes in 0 blocks
  24. ==6570== Rerun with --leak-check=full to see details of leaked memory
  25. ==6570==
  26. ==6570== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
  27.  
I have following code to create various forms in myproject.lpr file:
Code: Pascal  [Select][+][-]
  1.  
  2. begin
  3.   RequireDerivedFormResource:=True;
  4.   Application.Initialize;
  5.   Application.CreateForm(TForm1, Form1);
  6.   Application.CreateForm(TForm2, Form2);
  7.   Application.CreateForm(TForm3, Form3);
  8.   Application.CreateForm(TForm4, Form4);
  9.   Application.CreateForm(TForm5, Form5);
  10.   Application.Run;
  11. end.
  12.  
Do I need to free them manually at time of exit from program?
« Last Edit: September 21, 2025, 05:08:13 am by rnfpc »

AlexK

  • Full Member
  • ***
  • Posts: 101
Re: How to find source of memory leak
« Reply #6 on: September 21, 2025, 05:18:33 am »
The program runs fully without any error or warnings even with -gh and -gl options. However, the memory leak problem is still persisting:

Heaptrc summary says all is perfect.

You have to use also -gv option for FPC to generate Valgrind compatible binary, it uses cmem unit that calls C's malloc and free that are traced by Valgrind.

Thaddy

  • Hero Member
  • *****
  • Posts: 18676
  • Jungle wars. And failing health it seems.
Re: How to find source of memory leak
« Reply #7 on: September 21, 2025, 08:03:34 am »
Do I need to free them manually at time of exit from program?
No, they are owned and free'd by the application.
Application.CreateForm() registers the ownership for the form to the application.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Hartmut

  • Hero Member
  • *****
  • Posts: 1046
Re: How to find source of memory leak
« Reply #8 on: September 21, 2025, 08:11:32 am »
@rnfpc: in your 1st post you show a lot of free-statements. Because you created all 'TLineseries' with owner 'Chart1', they will be automatically freed at the time, when 'Chart1' is freed. So it is not neccessary, that you free them manually. Except if you would want to free these 'TLineseries' for the goal, to re-create them again afterwards, without freeing 'Chart1'. Same story for 'TextMemo' which has owner 'abgform'. But this should not be the reason for your memory leak.

I'm only a beginner to valgrind. I would recommend to use it's parameter '--leak-check=full'. This should show memory leaks as messages of this type:
520 bytes in 13 blocks are definitely lost in loss record 8,631 of 9,037

However I got for my program 22 complaints of this style, but 21 of them did not contain even 1 line of my sources. So maybe you have to check an extensive output...

Of course valgrind needs that you compile your program with option -gv.

I have following code to create various forms in myproject.lpr file:
Code: Pascal  [Select][+][-]
  1.  
  2. begin
  3.   RequireDerivedFormResource:=True;
  4.   Application.Initialize;
  5.   Application.CreateForm(TForm1, Form1);
  6.   Application.CreateForm(TForm2, Form2);
  7.   Application.CreateForm(TForm3, Form3);
  8.   Application.CreateForm(TForm4, Form4);
  9.   Application.CreateForm(TForm5, Form5);
  10.   Application.Run;
  11. end.
  12.  
Do I need to free them manually at time of exit from program?
No, because they are created with owner 'Application'. They all will be freed automatically, when your program terminates, when 'Application' is freed.

Thaddy

  • Hero Member
  • *****
  • Posts: 18676
  • Jungle wars. And failing health it seems.
Re: How to find source of memory leak
« Reply #9 on: September 21, 2025, 08:16:39 am »
Heaptrc summary says all is perfect.
Heaptrc is usually correct. Valgrind can give some false positives for perceived "leaks". You should only use it as a last resort.
I don't see any output (or partial output or zipped output) from valgrind with --leak-check=full .
We need that to see of valgrind is barking up to the wrong tree again.
Valgrind is more thorough, but has a lot of hallucinations, so we need the --leak-check=full  output to see why it thinks it found leak(s).
« Last Edit: September 21, 2025, 08:18:46 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

rnfpc

  • Full Member
  • ***
  • Posts: 118
Re: How to find source of memory leak
« Reply #10 on: September 21, 2025, 10:56:34 am »
I just dropped TChart on the Form in Lazarus GUI. In the unit pascal file 'chart1' is just declared and used as follows:
Code: Pascal  [Select][+][-]
  1. type
  2.    ...
  3.     Chart1: TChart;
  4.    ...
  5.  
  6. procedure ...
  7.     ...
  8.     Series := TLineSeries.Create(Chart1);
  9.     Chart1.AddSeries(Series);
  10.     ...
  11. end;
  12.  
I have also not freed 'chart1' at any place.
Do I need to manually 'create'  and 'free' it in source code?

wp

  • Hero Member
  • *****
  • Posts: 13329
Re: How to find source of memory leak
« Reply #11 on: September 21, 2025, 11:34:38 am »
Any component that you drop on the form in the designer is destroyed automatically. Moreover, any component that you create at runtime with an non-nil owner (someComponent := TSomeComponent.Create(SomeOwner)) is destroyed automatically by this owner - that's the purpose of the owner mechanism.

So, referring to the code snipped in the first post, it is not necessary to free the series and the memo (the series are destroyed by their owner, the chart, and the memo is destroyed by its owner, the form abgform). It IS necessary, however, to destroy the stringlist because it has no owner.

rnfpc

  • Full Member
  • ***
  • Posts: 118
Re: How to find source of memory leak
« Reply #12 on: September 21, 2025, 01:17:08 pm »
It seems that the most important part of valgrind output is:
Code: Pascal  [Select][+][-]
  1. Heap dump by heaptrc unit of .../mycalc_prog
  2. 12446 memory blocks allocated : 2541877/2560584
  3. 12446 memory blocks freed     : 2541877/2560584
If this is  ok, all is well.
One can ignore the leak summary.

Also I find that of the following procedures, FormCreate procedure is called  but not the FormDestroy procedure:
Code: Pascal  [Select][+][-]
  1. procedure TabgForm.FormCreate(Sender: TObject);
  2. procedure TabgForm.FormDestroy(Sender: TObject);
  3.  
How can one use FormDestroy procedure?
« Last Edit: September 21, 2025, 01:23:54 pm by rnfpc »

Hartmut

  • Hero Member
  • *****
  • Posts: 1046
Re: How to find source of memory leak
« Reply #13 on: September 21, 2025, 02:38:19 pm »
How can one use FormDestroy procedure?
I assume that your TabgForm.FormDestroy() is the OnDestroy-Event of 'TabgForm'.

AFAIK one should never call a destructor Destroy() directly. It is automatically called by Free().
The OnDestroy-Event is called by destructor Destroy(). You need it only to destroy components, which you have created with owner 'nil', because these are not freed automatically.

If you are unsure, whether an OnDestroy-Event is called or not, you can e.g. place temporarily a writeln('OnDestroy is called') command in it.

Thaddy

  • Hero Member
  • *****
  • Posts: 18676
  • Jungle wars. And failing health it seems.
Re: How to find source of memory leak
« Reply #14 on: September 21, 2025, 02:59:52 pm »
I assume that your TabgForm.FormDestroy() is the OnDestroy-Event of 'TabgForm'.
And so how would you guarantee the "event" is processed? It just means a message arrived. There is no guarantee otherwise.
That is fundamental to understanding a message based framework.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

 

TinyPortal © 2005-2018