Really Weird Actions in Fastmm5 using a var named Dlist

Maybe someone can explain this but I am at a loss.

I am using FastMM5 while debugging my code and on stepping past a free instruction I received a “virtual method was called on a freed object” error. Spent some time trying to resolve the issue. Gave up and did some gardening before returning and spending more time. Finally fixed by changing the variable name from Dlist to ThisList.

When stepping through code with DList.Free FastMM5 raises the error.

I was able to reproduce it in this simple example.
Renaming Dlist fixes the problem
Not breaking and stepping in the routine avoids the problem

procedure TForm8.TestDlist;
Var
  DList:TStringlist;
begin
  DList:=TStringList.Create;
  DList.Free;
end;



The stack trace for the virtual call that lead to this error is:
00421C86 [FastMM5.pas][FastMM5][TFastMM_FreedObject.VirtualMethodOnFreedObject$qqruc][3869]
00421D63 [FastMM5.pas][FastMM5][TFastMM_FreedObject.VirtualMethod5$qqrv][3898]
001E0036 
00D8D6BD [VCLComObjEdit.pas][VCLComObjEdit][TForm8.RefreshData][416]
00D8CFD1 [VCLComObjEdit.pas][VCLComObjEdit][TForm8.FormCreate][360]
1 Like

This is a funny part of Delphi
Replace TStringList.Create for TStrings.Create and it will work
some one may explain why
you should fund all of TStringList properties able to work still because its decleared
var
DList: TStringList

Have you tried forcing a build of the project?

You will notice many objects 1st contain a custom object and then the final object
this locks in the allocation of memory to the object in behind the final object that the allocation is not lost to something else.
var
DList: TStringList
makes the allocation lock when using the object
You will notice Delphi code use TCustomForm as well so it happens quite often
just do a search in google TStringList TStrings and TCustomForm TForm and read

Tried every thing:. Rebuild, Used IDE to check the scope of DList, fixed up many re-entrant calls caused by the routine called by Component.OnChange actually changing the Component. .

Now I have created an app with a new form and minimal dependencies and only one function

procedure TForm2.FormCreate(Sender: TObject);
Var
  DList,NList:TStringlist;
begin
  NList:=TStringList.Create;
  NList.Free;
  DList:=TStringList.Create;
//  DList.Free;
  NList:=TStringList.Create;
  NList.Free;
end;

As expected FastMM5 flags an “Unexpected Memory Leak” on closing with the DList.Free commented out but no errors stepping through the code,

With the comments removed no errors occur unless a breakpiont is set after DList.Free.

procedure TForm1.FormCreate(Sender: TObject);
Var
  DList,NList: TStringlist;
begin
  DList := TStringlist.Create;
  NList := TStringlist.Create;
  DList.Free;
  NList.Free;
end;

This complies with Delphi 11 perfectly

Hi Lex
My issues are not with the code compiling or even running.
I am at the stage where I wish to try to establish that there are no “double frees” and/or memory leaks as the code is required to run without failure for days or weeks.
To do this I have set up my program to run with FastMM5 memory management and enabled detailed error reporting.

By changing the name of the variable I am able to move on with my project but given that the issue drove me stupid for the best part of a day it seemed it was worth commenting on.

Using Alexandria, no plugins, latest FastMM4 with default settings, Win32, VCL, I do not see this issue at all – it steps through without a hitch.

You may have some 3rd-party units / plugins doing something strange. Try another installation of Delphi, hopefully clean with no addons.

PS: I had fun with names before, using some 3rd-party things, where they must have been using some internal names deep under the hood (i.e.: I could not see it in the provided code and the compiler was not giving any such warnings), which conflicted with what I used (and at first I used generic names, like “data”, which are more likely to clash than, say, “hgrnMySpecialDataVariable”). There were no actual errors, but the end result did not work as expected, until I have renamed all of them.

Alex

You create an object and its recorded as a pointer in Delphi ok
when not in use or after you have freed it set it to Nil

Recording the value as nil
If MyObject = nil then MyObject = TMyObject.create;

If MyObject <> nil then begin
MyObject.Free;
MyObject := nil;
end;
So you are using the object pointer as a created or not created state as well
very standard Delphi practice by all

I believe the issue (if it exists) is with FastMM5. Years ago I delved into FastMM4 and at that time it was the default memory manager for Delphi. When I went looking for this testing project FastMM5 was offered and claimed compatibility and improvements.
I spent some time working out the runtime configuration aspects but settled on a single routine which I now use

Procedure LoadFastMMfromISLib(ADeletePrevLog, AShowViaLog, AShowViaDialog,
 AShowViaDebugString,AEnterDebugMode : Boolean = true);
begin
  {$IFDEF TestFastMM}
  case FastMM_GetInstallationState of
    mmisDefaultMemoryManagerInUse:Exit;
    {Another third party memory manager has been installed.}
    mmisOtherThirdPartyMemoryManagerInstalled:Exit;
    {A shared memory manager is being used.}
    mmisUsingSharedMemoryManager:Exit;
    {This memory manager has been installed.}
    mmisInstalled:Begin
    if ADeletePrevLog then
      FastMM_DeleteEventLogFile;
    End;
  end;


if AEnterDebugMode then
  if FastMM_LoadDebugSupportLibrary then
    FastMM_EnterDebugMode;


  if AShowViaLog then

  FastMM_LogToFileEvents := [
  { Another third party memory manager has already been installed. }
    mmetAnotherThirdPartyMemoryManagerAlreadyInstalled,
  { FastMM cannot be installed, because memory has already been allocated through the default memory manager. }
  mmetCannotInstallAfterDefaultMemoryManagerHasBeenUsed,
  { When an attempt is made to install or use a shared memory manager, but the memory manager has already been used to
    allocate memory. }
  mmetCannotSwitchToSharedMemoryManagerWithLivePointers,
  { Details about an individual memory leak. }
  mmetUnexpectedMemoryLeakDetail,
  { Summary of memory leaks }
  mmetUnexpectedMemoryLeakSummary,
  { When an attempt to free or reallocate a debug block that has already been freed is detected. }
  mmetDebugBlockDoubleFree, mmetDebugBlockReallocOfFreedBlock,
  { When a corruption of the memory pool is detected. }
  mmetDebugBlockHeaderCorruption, mmetDebugBlockFooterCorruption,
    mmetDebugBlockModifiedAfterFree,
  { When a virtual method is called on a freed object. }
  mmetVirtualMethodCallOnFreedObject]
  Else
    FastMM_LogToFileEvents := [];

  if AShowViaDialog then
  FastMM_MessageBoxEvents :=
    [mmetDebugBlockDoubleFree,
    mmetDebugBlockReallocOfFreedBlock, mmetDebugBlockHeaderCorruption,
    mmetDebugBlockFooterCorruption,
    mmetDebugBlockModifiedAfterFree, mmetVirtualMethodCallOnFreedObject,
    mmetAnotherThirdPartyMemoryManagerAlreadyInstalled,
    mmetCannotInstallAfterDefaultMemoryManagerHasBeenUsed,
    mmetCannotSwitchToSharedMemoryManagerWithLivePointers]
   Else
     FastMM_MessageBoxEvents := [];

  if AShowViaDebugString then

  FastMM_OutputDebugStringEvents :=
   [mmetDebugBlockDoubleFree,
    mmetDebugBlockReallocOfFreedBlock, mmetDebugBlockHeaderCorruption, mmetDebugBlockFooterCorruption,
    mmetDebugBlockModifiedAfterFree, mmetVirtualMethodCallOnFreedObject, mmetAnotherThirdPartyMemoryManagerAlreadyInstalled,
    mmetCannotInstallAfterDefaultMemoryManagerHasBeenUsed, mmetCannotSwitchToSharedMemoryManagerWithLivePointers]
    else
  FastMM_OutputDebugStringEvents := [];

  ReportMemoryLeaksOnShutDown:=true;
{$Endif}
end;

In the test program demonstrating the error there is no reference to anything other than VCL Forms and the FastMM5 code.

Not sure whether Delphi now uses FastMM5 or FastMM4 or its own memory manager by default but I am (normally) very happy using FastMM5 while testing for leaks and memory errors. I use the Delphi default for release.

If I get a “virtual method was called on a freed object” error in future I will try changing the variable name before spending hours searching for a buried free.

1 Like

I have not dealt with FastMM5
The speed changes you cannot measure any way
You can only build something with your capacity of how you can use Delphi and its objects
Delphi is fast because it starts from low level
Delphi is stable
Its very English language focused with many safety measures to code errors
c++ is not
c# is not
Its windows and Linux
Its very data base access focused
Its very front end focused
and has a wide scope of support with all this
So start asking what can I Create with that support
Don’t get tied up with FastMM5 and side issues that have pour support
Tie your self up with what can I make with one skill at a time until you release what you can make. then make it your own.

The default MM is still FastMM4. FastMM5 has a different license - GPLv3 or Commercial (you need to pay).

Make a sellable project first to pay for things
Then consider things like FastMM4

1 Like

With the greatest genuine respect I can muster - that’s utter nonsense. Sorry.

He’s trying to ensure that what he delivers is robust and logically correct. FastMM4 is free and included in RAD Studio and it’s a tool used to both manage memory and indicate when something has been done which is syntactically correct but logically incorrect.

For example:

var
  MyObject: TMyObject;

procedure Test;
var
  Int1, In2: integer;
begin
  MyObject := TMyObject.Create;
  try
    Int1 := 0;
    Int2 := iInt1 div 10;
    // Never gets to the following line...
    // So MyObject doesn't get correctly freed   
   FreeAndNil(MyObject);
  except
    Log('Divide by zero error');
  end;
end;

There are lots of other ways of getting things like orphaned memory and hanging handles. FastMM will report on those. Checking for that kind of thing is standard practice for professional coders, especially if the tool to do it is free and included.

syntactically is the word
The bottom line is the application needs to utilize its space well
all computer programming is built around Logic
the use of TList to record what object is opened and to delate it from the list when its closed is logic and TForm controls its screen objects this way - the use of memory is controlled in logic so how do you get away from that?
TStringlist does not allocate memory to hold its strings is this were you are going

Hmm, you’re trying to correct my English? :rofl:

Lex, I’m not sure if there’s a lack of basic knowledge or a communication difficulty, but you’re wrong. Syntax pertains to the rules of the keywords and language constructs - and, in fact, you can recognize that to be true because the compiler and parser will flag syntax errors, similar to spelling errors, generally at the time you write or attempt to compile the code.

Logic errors are different. They are where the developer has done something that generally compiles correctly and may even run - but eventually might or will lead to unwanted behavior. Generally we think of unwanted behavior as a bug.

In the example I gave it is syntactically correct to write the code as I have and it will compile without errors, but it will lead to a memory leak because I deliberately try to divide by zero, which then triggers the code to jump over the line which would free the object and this causes a memory leak.

It’s syntactically correct code, but logically incorrect because an experienced programmer would usually spot the mistake and fix it by reordering the FreeAndNil statement. I was using it to illustrate a kind of memory leak that FastMM would capture and report - and it will do that for free with most recent versions of RAD Studio.

The FreeAndNil(MyObject); is just in the wrong place
and a good programmer would never leave it there and that’s obeying good logic practice

Again if myObject = nil then myObject = TmyObject.create;
so you do not create over the top of a old object and reuse the pointer

myObject is nothing more than a pointer and Delphi gives it a title to separate it
1/ object type
2/ naming it to separate it from others of the same type

what I’m saying is syntactically is not the right word
every name is allocated to a memory location that contains a type of data in Delphi
you can call it extended algebra if you like its what the complier does so man can track the data being assembled.
it has nothing to do with divide by zero

I’m also saying the create constrictor is a fancy function in the Delphi code that returns a object pointer.

1 Like

What I see as a down fall of the Delphi complier
is the stack pointer records the top of the stack of procedure data
when an object is created with TCustomMyObject of the object and then the main object TMyObject pointer is added on top of all that stack, that the complier then considers the top of the stack. Without the final TMyObject pointer the object data in TCustomObject is written over or corrupted with additional data to the stack.
So a object created in a procedure and deleted in the same procedure is fine and a object created at initialization and deleted in finalization is fine but a object created out of these spaces I have no idea how they are managed.
So I don’t know what FastMM does bat can be something relating to this is possible
.

Understood.

Funny that nobody pointed out that 0 div 10 is not a div by zero :wink: @ianbarker

2 Likes