2
votes

FastMM reports a memory leak (UnicodeString) for the following code snippet that uses a record thread variable with a string:

program Project10;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  FastMM4,
  System.SysUtils;

type
  TContext = record
    Value : String;
  end;

threadvar
  Context : TContext;

begin
Context.Value := 'asdfsdfasfdsa';
end.

Is this a real memory leak or is the cleanup of thread variables just happening after FastMM checks for memory leaks?

Even more important: how can I suppress these "memory leaks" being reported as they clutter up any other memory leaks that might be found?

2
I believe the thread must cleanup dynamic values? Try to add Context.Value := '' in thread cleanup code. - whosrdaddy
This is part of my logging code, so I cannnot control thread cleanup since this might be called from any thread. - jpfollenius
If you don't have access to the threads, then why are you using thread variables to begin with? What are you using them for exactly? Since you don't control the threads, the only other way I know to detect thread terminations is to write and load a DLL so you can receive DLL_THREAD_DETACH notifications. But then the DLL would need to manage the thread variable memory, unless you share FastMM across the DLL boundary. - Remy Lebeau
@Remy You would not need to share FastMM in that situation. The DLL would be trivial. Load it up and call a function that it exports passing callbacks to receive the notifications. Then those notifications can run in the context of the host. - David Heffernan
@DavidHeffernan Well I tried and it does definitely report a leak on XE2. Note that I include FastMM4 unit. - jpfollenius

2 Answers

6
votes

That's a real leak. Thread local variables are not finalized when they go out of scope. Because your record contains a field that is managed, the string field, the heap allocated memory associated with that string is leaked if the record is not finalized.

The documentation calls this out explicitly:

Dynamic variables that are ordinarily managed by the compiler (long strings, wide strings, dynamic arrays, variants, and interfaces) can be declared with threadvar, but the compiler does not automatically free the heap-allocated memory created by each thread of execution. If you use these data types in thread variables, it is your responsibility to dispose of their memory from within the thread, before the thread terminates.

If you want to plug the leak, you'll need to finalize the variable as the scope ends. That is, as the thread is terminating.

Finalize(Context);

Note that you must execute this code from the thread which owns the variable since, obviously, only that thread has access to it.

If you want to suppress reporting of these leaks then call RegisterExpectedMemoryLeak.

If you cannot execute code when threads terminate then it may be better to avoid heap allocation and use a fixed length character array. Quite possibly that you meet your needs.

It would seem odd that you claim not to be able to execute code when the threads terminate. If you cannot do that, how are you able to execute any code in the context of these threads. In other words, in order for there to be a leak, you must have executed your code in these threads.

0
votes

Create global array of TContext, and then store index of the element, that belongs to your thread, in a threadvar.