Jump to content

GarbageCollector class - what do you think?


elGringo

Recommended Posts

look at this class

unit uGC;

interface

uses
  System.Generics.Collections, Rtti, System.Classes;

type
  TGarbageCollector = class(TComponent)
  public
    const
      DEFAULT_TAG = 'DEFAULT_TAG';
  private
    items: TDictionary<TObject, string>;
  public
    destructor Destroy; override;
    constructor Create(AOwner: TComponent); override;
    function Add<T>(item: T): T; overload;  
    function Add<T>(item: T; const tag: string): T; overload;  
    procedure Collect(const tag: string);
  end;

var
  GC: TGarbageCollector;

implementation

uses
  System.Types, System.SysUtils;


constructor TGarbageCollector.Create(AOwner: TComponent);
begin
  inherited;
  items := TObjectDictionary<TObject, string>.Create([doOwnsKeys]);
end;

destructor TGarbageCollector.Destroy;
begin
  items.free();
  inherited Destroy;
end;

function TGarbageCollector.Add<T>(item: T): T;
begin
  result := Add(item, DEFAULT_TAG);
end;

function TGarbageCollector.Add<T>(item: T; const tag: string): T;
var
  obj: TObject;
  v: TValue;
begin
  v := TValue.From<T>(item);
  if v.IsObject then
  begin
    items.add(v.AsObject, tag);
    result := item;
  end
  else
    raise Exception.Create('not an Object');
end;

procedure TGarbageCollector.Collect(const tag: string);
var
  key: TObject;
  item: TPair<TObject, string>;
  gcList: TList<TObject>;
begin
  gcList := TList<TObject>.Create();
  try
    for item in items do
    begin
      if (item.Value = tag) then
        gcList.add(item.Key);
    end;

    for key in gcList do
      items.remove(key);
  finally
    gcList.free();
  end;
end;

end.

Create it

program GarbageCollector;

uses
  Vcl.Forms,
  uMain in 'uMain.pas' {Main},
  uGC in 'uGC.pas',
  uSomeClass in 'uSomeClass.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  GC := TGarbageCollector.Create(Application); // <<<
  Application.CreateForm(TMain, Main);
  Application.Run;
end.

use it

  someInstance := GC.Add(TSomeClass.Create(nil), 'TSomeClassTag');
  // do smth with someInstance
  //now destroy
  GC.Collect('TSomeClassTag');
  //
  anotherInstance := GC.Add(TSomeClass.Create(nil), 'TSomeClassTag');
  // do smth with anotherInstance
  // not destroying here - will be destroyed on app destroy...

what do you think?

Link to comment
Share on other sites

Hi. 

For me it is not good idea. What do You want to achive ? Cleaning memory after application close ?

For me leak is mistake / error and I want to repair all this mistakes. I use fastmm or similar solution with full debug mode. All leaks should be repaired at place where they appear. 

Cleaning all leaks at app close is dirty solution. Your app can work long time without restart and it should consume as small amount of memory as possible.

Link to comment
Share on other sites

hm... agree with you, just it was start for discussion about the memory management in Delphi.

So, could you say how do you look for memory leaks with fastMM - i don't know that for the moment.

I use only 

ReportMemoryLeaksOnShutdown := true;

But this approach only says me that there is a memory leak and what is leaking TStringList or etc...

But how, for example you find memory leak in special exact place of code?

Regards, Stan

Link to comment
Share on other sites

and besides, my topic start code here gives 2 possibilities

-manage code manually as usual, by grouping instances by tag

-100 % clear on App Close if you have forgotten to clear smth inside the scope (protection from human factor...)

Regards...

Link to comment
Share on other sites

And what about this example? Wrapper for classes, based on ARC ? Looks smart )))

So, you may avoid try... finally... and just Create Use Forget )))

unit uSmartPointer;

interface

type
  ISmartPointer<T> = reference to function: T;

  TSmartPointer<T: class, constructor> = class(TInterfacedObject, ISmartPointer<T>)
  private
    FValue: T;
    function Invoke: T;
    procedure SetValue(const Value: T);
  public
    constructor Create; overload;
    constructor Create(AValue: T); overload;
    destructor Destroy; override;
    function Extract: T;
    property Value: T read FValue write SetValue;
  end;

implementation

constructor TSmartPointer<T>.Create;
begin
  inherited Create;
  FValue := T.Create;
end;

constructor TSmartPointer<T>.Create(AValue: T);
begin
  inherited Create;
  FValue := AValue;
end;

destructor TSmartPointer<T>.Destroy;
begin
  FValue.Free;
  inherited;
end;

function TSmartPointer<T>.Invoke: T;
begin
  Result := FValue;
end;

procedure TSmartPointer<T>.SetValue(const Value: T);
begin
  FValue := Value;
end;

function TSmartPointer<T>.Extract: T;
begin
  Result := FValue;
  FValue := nil;
end;

end.

Example of use

procedure Smart;
var
sl: ISmartPointer<TStringList>;
begin
sl := TSmartPointer<TStringList>.Create();
sl.Add('I am inside automanaged StringList');
end;

 

Link to comment
Share on other sites

On 10/30/2018 at 5:50 AM, elGringo said:

But how, for example you find memory leak in special exact place of code?

Regards, Stan

Hello Stan

I’m not at screen at this moment so I give You only brief. 

First You have to enable FullDebugMode in FastMM configuration file. You have to remove dot at proper line. 

Next You have to enable „include debug info” at compilator options. After next build You can execute app. During app close, if any leak will be detected You will see detailed log file with code lines whitch makes leaks. 

Don’t forget to disable including debug info in next builds because it makes exe much more bigger in size. 

Best regards

Jaromir

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...