Forum > GTK

[solved] Unreleased DCs and unreleased GDIObjects - TImage memory leaks

<< < (3/4) > >>

andyH:
Finally!!!! I fixed it, but not through any of the suggestions above - although they led me to trying every permutation and combination I could think off = two days work. The problem was the TImages on the form - these were not being freed when the form was freed.

I would like someone with more knowledge than me to explain why. I amended the code creating the form, based on the above to:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---            try              ConfirmForm:= Tconfform.create(MainFm);              //populate the confirm form, first the source drive              ...more setup code for the form              ConfirmForm.ShowModal;              writeln('line before modalresult test........');              if ConfirmForm.modalResult = mrOK then writeln('show modalresult triggered.......');            finally              FreeAndNil(ConfirmForm);            end; No change from previous behaviour. I also tried changing Tconfform.create(MainFm) to Tconfform.create(Self) and got backup.pas(293,44) Error: Identifier not found "Self", why???
 
Changing the finally statement to:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---            finally              ConfirmForm.cDriveImg.Free;              ConfirmForm.cFileImg.Free;              ConfirmForm.cArrowImg.Free;              FreeAndNil(ConfirmForm);            end;Fixed the problem - no memory leaks on exit.

As an example, code for the constructor for ConfirmForm for one of the images is:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---  cFileImg:=TImage.Create(ConfirmForm);  with cFileImg do       begin         Name:='File';         Parent:= Self;         left:= 20;         top:= 115;         autosize:= false;         Picture.LoadFromFile('/home/andy/backupGUIV7/file2-64.png');       end;I did try omitting the parent statement, setting it to nil and setting it ConfirmForm, in each case it did not display on the form (same was true for every other object on the form)?

What was confusing me was that I've got other 'pop up' forms that were not causing any problems, I had free statements on them when finished = no memory leaks. The only difference - no TImages. So why are TImages causing a memory leak when the parent form is correctly freed??

As an 'on and off' pascal user since the mid '80's I think lazarus is brilliant, but I do find issues like this very frustrating - members of this forum have to give their time freely to fill the gaps, some of which could be avoided through decent documentation.

Finally, the form that has been causing me so much trouble, with the offending images    :)

lucamar:
It would probably have worked as it should if instead of:

--- Code: ---  cFileImg:=TImage.Create(ConfirmForm);
--- End code ---
you had used

--- Code: ---  cFileImg:=TImage.Create(Self);
--- End code ---

You should never tie a control (or anything else, for that matter) to a specific object instance but to the current one, which is always accessible through Self.

That said, it's never a bad idea to free in the destructor whatever you create in the constructor, even if it's supposed to be freed automatically.

devEric69:
lucamar is right.

Here's a little "topo", concerning memory management. There are few golden rules to which, we must adhere, if we want to manage memory well:
- the place where memory has been allocated (
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---new(pMyRecord); or
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---oMyObject.create(TheOwnerIfAllowed) ), is the place where this memory must be released (
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---dispose(pMyRecord); ptr:= nil;, or
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---FreeAndNil(oMyObject);, or
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---oMyObject.Free; oMyObject:= nil ).
==> This means that the allocations in the heap that are generally made at the beginning of a procedure, must be made in the opposite order of the allocations, at the end of the same procedure: the code can be refactored by calling subroutines, but always in order to respect this golden rule!
- if sub-components are created by encapsulation in a proprietary component, what is created in the proprietary component's constructor must be destroyed in the opposite direction of their construction, in the proprietary component's destructor.

Taking into account what I just said, your images are contained in your dialog box: so, if you create them in your formCreate event, you must destroy them in the formDestroy event.

By sticking to these simple rules, and assigning nil to pointers whose memory has been released (use FreeAndNil wherever you can; otherwise use dispose or Free and then, think to assign just behind the pointer with nil!), this allows you to destroy testing with
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---if Assigned(oMyObject) then FreeAndNil(oMyObject);
Then, to go further in the understanding of Free Pascal, there are two important notions whose documentation must be read:
- the owner (which is either the first AOwner parameter passed to a constructor), or a TForm or Tframe on which a control is placed, by drop from the pallet. The owner has the responsibility to destroy everything he owns: it avoids calls to FreeAndNil. That said, if you are a little paranoid, the rules explained above should be able to continue making "safe" paranoid calls to FreeAnNil in the places I've indicated (which are bijective in places of their explicit creation).
- the parent: the parent of a visual control is simply the visual component on which your visual component is  placed on. A control (ie a visual component) can therefore jump visually from one control to another, if you change programatically its parent property (for example, it can jump from a container like TPanel towards another like TGroupBox). That's all: it has nothing to do with memory management.

I'm going to say it again: you really have to do everything, but really do everything - otherwise, it will come back to you "in your teeth" sooner or later - to set your pointers with nil after having misallocated the memory on which they pointed, because internally, their Free and Destroy methods also use a test similar like:
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---if (pMyRecord <> nil) then "release"
If the pointer still has an address in the heap but its content has been released (but it's not constant: it depends on what runs in addition with Laz. on your PC, the memory previously allocated can be partially reset with other things), then its destruction will result in a "memory access violation" at 99% .

>:D ==> by not assigning with nil your "unallocated" pointers, you will go straight towards fuck*** dangling pointers problems, with "memory access violation".

(one last thing: there is also the management of interface memory, which is a completely different world. I say that, in case you are asked to use them to create \ use a COM server, or to do behavioral polymorphism's logic, instead of inheritance polymorphism's logic, and if you are prevented from using Helper for TMyClass that allow you to stay in inheritance polymophism's logic).

andyH:
Each day I learn a little more, thanks for the detailed explanations. In future I will use freeandnil instead of tobject.free. Also thought I'd tried cFileImg:=TImage.Create(Self) as I stumbled towards a solution. Obviously not, as I tried it and it worked. %)

RAW:

--- Quote --- ...just wish there was a resource somewhere that wrote all this down  :(
--- End quote ---
Try this: https://castle-engine.io/modern_pascal_introduction.html
Or if you like pdf's then this: https://castle-engine.io/modern_pascal_introduction.pdf

 :)

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version