13

I just installed Delphi 10.2 Release 1. When I recompiled my applications and ran them, I get a lot of memory leaks. I had no memory leaks with 10.2 (without the update). I made no changes to the code either.

To verify, I created a simple blank application and put a few components on the form. No code. Ran the application and got memory leaks reported. Memory leaks from the sample application

I wanted to highlight this (if only as a warning before you upgrade).

My questions:

  1. Has anyone else seen this issue?
  2. Is there something I need to or could be doing to get rid of this issue?

Note: I have logged an issue on quality portal, just in case this is a real issue: https://quality.embarcadero.com/browse/RSP-18774. In this ticket I have also attached the sample app.

Rohit
  • 909
  • 1
  • 9
  • 20
  • Seems TCalendar creates leaks. – Andrei Galatyn Aug 09 '17 at 11:33
  • @AndreiGalatyn no, I have tested with the app without TCalendar and leaks appeared :( – zdzichs Aug 09 '17 at 11:43
  • I have compiled various applications with 10.2.1 and all of them report memory leaks (where there were none before upgrading). Removing the calendar component does not eliminate the memory leaks. Compiled to Win32 and Win64 - both show memory leaks. – Rohit Aug 09 '17 at 11:50
  • Single memo on the blank form is enough to see the leak! But the form with two buttons, toolbar and speedbuttons does not leak. For the other app I get leak messages when the app deals with bitmaps. Maybe it has something to do with: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Multi-Threading_for_TBitmap,_TCanvas,_and_TContext3D ? And more: changing ContolType for a memo to Platform gives no leak. – zdzichs Aug 09 '17 at 13:48

2 Answers2

18

After some investigation I found out that the callbacks being passed to TThread.CurrentThread.ForceQueue in TStyledControl.KillResourceLink are never executed because before any thread can handle them the application is ending and the TThread class destructor is destroying the list that still has unhandled callbacks.

I solved this by adding a call to CheckSynchronize at the end of FMX.Forms.DoneApplication which forces the callbacks to be executed which resolved the huge memory leak.

I don't know if this is the correct fix for the issue but it solved the memory leaks being reported.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
  • Is `ForceQueue()` being called with a nil `AThread` parameter? If so, then queued callbacks stay alive after the queuing thread is freed. If a thread object is specified, any callbacks it queues are freed when itself is freed. But this sounds like a bug in the queuing mechanism if it is destroying the queue list without destroying any items in the list. File a report with Embarcadero, as this will likely affect `TThread.Queue()` as well. `ForceQueue()` is new in Tokyo, so this is probably just an oversight in the new implementation of the queuing (I don't have Tokyo so I can't verify). – Remy Lebeau Aug 09 '17 at 15:38
  • If you think there is a bug and you were involved in the initial development (RSP-15427) then you can report something. Ironically it is called like this: `TThread.CurrentThread.ForceQueue(nil, ... );` wth `CurrentThread` though? Its a static class procedure ;) – Stefan Glienke Aug 09 '17 at 15:39
  • 1
    I'm the one who [requested a `ForceQueue`-like feature](https://quality.embarcadero.com/browse/RSP-15427) be added, but my suggested approach was very different than what Embarcadero actually implemented. I wasn't involved in its development. – Remy Lebeau Aug 09 '17 at 15:44
  • Whatever. I am not reporting things I don't even know they are broken because I don't have a clue about them. You seem to be more qualified. – Stefan Glienke Aug 09 '17 at 15:48
  • yes, calling it as `TThread.CurrentThread.ForceQueue(nil, ... );` is redundant when calling it as `TThread.ForceQueue(nil, ... );` works. Embarcadero is a bit over-zeleous in its use of `CurrentThread` (which isn't even safe to use at times). – Remy Lebeau Aug 09 '17 at 15:53
  • @StefanGlienke - thanks for this. I tried it by adding `CheckSynchronize;` to the end of the `DoneApplication` procedure in FMX.Forms, but my issues persist. I tried a clean and build as well but still no luck. Something I may be missing? – Rohit Aug 10 '17 at 03:37
  • Never mind - I figured it out. Just making the change to the source file was not sufficient as the application was still picking up the DCU from the lib folder. So basically I had to copy the FMX.Forms.pas file (with the code change) to my project directory and build the application and then put the generated DEBUG and RELEASE DCU files to their appropriate directories under the lib folder (for both Win32 and Win64). Once I did this, the issue seems to have gone away. Many thanks to @stefanglienke for this solution. I would not have known where to start looking to try and fix this issue. – Rohit Aug 10 '17 at 04:07
  • 5
    On a side note - it would be good if Embarcadero tested the releases they are putting out - we need to be able to reliably use the software (with confidence) and not have to deal with such issues in a release version of the application. – Rohit Aug 10 '17 at 04:08
  • 1
    Blog post "Workaround for FMX Memory Leaks on Windows in 10.2.1" at http://blog.marcocantu.com/blog/2017-august-workaround-fmx-memleak.html (thanks Stefan!) – Marco Cantù Aug 16 '17 at 08:59
0

I have the same problem using C++Builder 10.2.1 in FMX and in VCL applications.

If I enable CodeGuard, I get memory leaks on application exit.

I have a TThread with OnTerminate handler: if I put a breakpoint in this handler, when I close the program it is never called.

If I put CheckSynchronize() in the destructor of my main application form, the problem remains.

My solution was a "horrible" loop like this in the destructor of the main form:

__fastcall TForm3::~TForm3(void) {
    for(int i = 0; i < 10; i++) {
        Sleep(1);
        CheckSynchronize();
    }
}

This solution is not deterministic but may be used in your application in debug mode to avoid CodeGuard error messages.

Another solution is using the WaitFor() function if MyThread is a TThread object:

MyThread = new MyThreadClass();

and DeleteThisTh() is a method of this class, we can wait for terminated thread inside DeleteThisTh():

void MyThreadClass::DeleteThisTh(void) {
    Terminate();
    WaitFor();
    delete this;
}

In the OnTerminate event, I can clean my objects. Take note:

  1. delete this is called after OnTerminate;
  2. DeleteThisTh() lives in the main thread;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770