Recent

Author Topic: Releasing heap memory confusion  (Read 444 times)

Anonimista

  • New Member
  • *
  • Posts: 19
Releasing heap memory confusion
« on: September 18, 2019, 05:58:10 pm »
I had a related question here:

https://forum.lazarus.freepascal.org/index.php/topic,46781.0.html

Simone helpfully pointed me to the heap trace utility. I am trying to use a generic container (TTree). Here is the code:

Code: Pascal  [Select]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.     gtree, sysutils;
  7.  
  8. type
  9.  
  10.     TExpression = class(TObject)
  11.     public
  12.         value: integer;
  13.         constructor Create(v: integer);
  14.     end;
  15.  
  16.     TExpressionNode = specialize TTreeNode<TExpression>;
  17.     TExpressionTree = specialize TTree<TExpression>;
  18.  
  19.  
  20. var
  21.     tree: TExpressionTree;
  22.     node: TExpressionNode;
  23.     e: TExpression;
  24.  
  25.  
  26. constructor TExpression.Create(v: integer);
  27. begin
  28.     self.value := v;
  29. end;
  30.  
  31.  
  32. begin
  33.  
  34.     e := TExpression.Create(1);
  35.     node := TExpressionNode.Create;
  36.     tree := TExpressionTree.Create;
  37.    
  38.     node.Data := e;
  39.     tree.Root := node;
  40.    
  41.     e.Free;
  42.     node.Free;
  43.     tree.Free;
  44.    
  45. end.
  46.  

The output is:

Code: Pascal  [Select]
  1. An unhandled exception occurred at $000000000041586D:
  2. EAccessViolation: Access violation
  3.   $000000000041586D
  4.   $0000000000415870
  5.  
  6. Heap dump by heaptrc unit
  7. 17 memory blocks allocated : 1156/1176
  8. 13 memory blocks freed     : 948/968
  9. 4 unfreed memory blocks : 208
  10. True heap size : 393216
  11. True free heap : 392384
  12. Should be : 392496
  13. Call trace for block $00007FBDECCE81E0 size 128
  14.   $0000000000416C61
  15.   $0000000000415870
  16. Call trace for block $00007FBDECCD0300 size 40
  17.   $0000000000416C61
  18.   $0000000000415870
  19. Call trace for block $00007FBDECCD0240 size 24
  20.   $0000000000415870
  21. Call trace for block $00007FBDECCD8200 size 16
  22.   $0000000000403671 line 37 of project1.pas
  23.  
  24.  
  25. ------------------
  26. (program exited with code: 217)
  27. Press return to continue
  28.  

So there is an access violation and not all memory is freed. I studied this guide:

https://www.freepascal.org/docs-html/ref/refse37.html#x74-960006.4

and tried FreeAndNil but the same thing happens. So it is obvious I lack a fundamental understanding of how should class instances should be freed. Any pointers where I can read upon this issue is appreciated. Thanks.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5765
    • wiki
Re: Releasing heap memory confusion
« Reply #1 on: September 18, 2019, 06:28:57 pm »
Node known by the tree are destroyed by the tree => check the source code.

So

Code: Pascal  [Select]
  1.     tree.Root := node;
  2.     node.Free;
  3.     tree.Free;

Frees  the node twice.

FreeAndNil does not help, because the reference is in 2 difference variables: node and tree.FRoot

If you destroy node yourself, you must remove it from the tree.


Note that "nodes" are destroyed recursively. The tree destroys "root", and "root" then destroys all its children, and they do destroy there children .....

That means, you can add any one "node" only once into one tree.
If you try to add the same node, as a child to multiple parents... boom.

Anonimista

  • New Member
  • *
  • Posts: 19
Re: Releasing heap memory confusion
« Reply #2 on: September 18, 2019, 06:59:58 pm »
Oh okay that makes sense. I remove node.Free and the heap trace shows all memory released. But what the data (TExpression)? If I remove e.Free the memory trace shows the e memory is not being released. Does this mean I should over-ride The TTreeNode destructor to free it's Expression instance? Here is the destructor code:

Code: Pascal  [Select]
  1. destructor TTreeNode.Destroy;
  2. var
  3.   Child: TTreeNode;
  4. begin
  5.   for Child in FChildren do begin
  6.     Child.Free;
  7.   end;
  8.   FChildren.Free;
  9. end;

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5765
    • wiki
Re: Releasing heap memory confusion
« Reply #3 on: September 18, 2019, 07:03:37 pm »
If you want it destroyed by the node, then yes, override it.

Unless you hold on to the expressions elsewhere, I would think it to be a good idea to override.

Anonimista

  • New Member
  • *
  • Posts: 19
Re: Releasing heap memory confusion
« Reply #4 on: September 18, 2019, 07:24:25 pm »
thanks!

Anonimista

  • New Member
  • *
  • Posts: 19
Re: Releasing heap memory confusion
« Reply #5 on: September 18, 2019, 08:14:21 pm »
Sorry to continue the thread, but I can not find an example of how to over-ride the destructor of a specialized generic class. Here is my attempt:

Code: Pascal  [Select]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.     gtree, sysutils;
  7.  
  8. type
  9.  
  10.     TExpression = class(TObject)
  11.     public
  12.         value: integer;
  13.         constructor Create(v: integer);
  14.     end;
  15.  
  16.     TExpressionNode = specialize TTreeNode<TExpression>;
  17.     TExpressionTree = specialize TTree<TExpression>;
  18.  
  19.  
  20.  
  21. var
  22.     tree: TExpressionTree;
  23.     node: TExpressionNode;
  24.     e: TExpression;
  25.    
  26.  
  27. constructor TExpression.Create(v: integer);
  28. begin
  29.     self.value := v;
  30. end;
  31.  
  32.  
  33. destructor TExpressionNode.Destroy;
  34. begin
  35.     self.Data.Free;
  36.     inherited;
  37. end;
  38.  
  39.    
  40. begin
  41.  
  42.     e := TExpression.Create(1);
  43.     node := TExpressionNode.Create;
  44.     tree := TExpressionTree.Create;
  45.    
  46.     node.Data := e;
  47.     tree.Root := node;
  48.    
  49.     e.Free;
  50.     {node.Free;}
  51.     tree.Free;
  52.    
  53. end.
  54.  

But I get this syntax error:

project1.pas(37,4) Fatal: Syntax error, "BEGIN" expected but ";" found

I am still trying to find my way around the documentation.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5765
    • wiki
Re: Releasing heap memory confusion
« Reply #6 on: September 18, 2019, 08:39:34 pm »
Eiter:
Code: Pascal  [Select]
  1.  TExpressionNodeBase = specialize TTreeNode<TExpression>;
  2.  TExpressionNode = class(TExpressionNodeBase)
  3.   destructor Destroy; override;
  4. end;
or
Code: Pascal  [Select]
  1.  TExpressionNode = class(specialize TTreeNode<TExpression>)
  2.   destructor Destroy; override;
  3. end;

Anonimista

  • New Member
  • *
  • Posts: 19
Re: Releasing heap memory confusion
« Reply #7 on: September 18, 2019, 09:14:12 pm »
Thank you!