System Resources - How Many Is Too Many?

I’ve just been looking at my application details in Task Manager - Handles, Threads, User Objects and GDI Objects.

Right now it has 1055 Handles, 26 Threads, 856 User Objects and 1761 GDI Objects.

How many is too many?

GDI Objects is the one that concerns me as some users are getting the occasional Out Of System Resources exception that follows a GDIError.

Sounds Cruizy.

The default limit is 10,000

In our App the user can do things that result in heaps of them being required.

We have only ever had one user exceed 10,000

The upper limit is 64k, you can google the registry entry to change easily enough. Note that there are two entries, I think it is 32 or 64bit apps, but it is not particularly clear which is which.

In an effort to reduce resource use I have just changed a few forms from Auto Create to creating them only as required then freeing them afterwards. Those forms are only used occasionally and often never in a some usage sessions. This reduced the base GDI Objects count by about 300.

I’m not sure how to track down the occasion Out Of Resources though as I have never been able to replicate the error on my systems.

Something I’ve just noticed is that the GDI Object count creeps by 2 each time I use (create and free) one particular form. It doesn’t sound like much, but I don’t like that it seems to leak resources. I’ve checked through the code and can’t see where I’m allocating anything that’s not being freed again. How would I go about tracking that down?

https://ddobjects.de

This package is extremely helpful for these types of tasks

It looks like TMS bought that out. Ironically the leak seems to be in the TMS grid when adding images.

Are you sure it’s a leak, it may take several to many GDI objects to ‘support’ each added image, maybe even more depending on what you can do with that image, or if it’s just a plain image vs composite.

Yes, if after deleting the image the GDI objects doesn’t go back down it’s probably a leak, but whilst it’s there it’s quite possible the increase is valid.

When the form containing the TMS grid and images is freed, all of the associated resources should be freed too though?

Should be.

But like a lot of ms ‘counters’ the count might not go down instantly, so you need to allow for a bit of inconsistency.

I’m actually looking at this issue for FinalBuilder 9 - which uses twice as many GDI objects as FinalBuilder 8 - FB8 was compiled with XE7, FB9 compiled with 11.2 - trouble is I have also updated every library so haven’t figured out what is using so many GDI objects yet - could be a library or could just be the VCL using more objects :thinking:

About your GDI resources creeping up, I have struggled with this one as well. I write reasonably complex multi-threaded applications (with minimal interfaces) that process/convert sizeable incoming data & audio every 5 mn, 24/7, and while I was always happy when a FastMM4 report only found a very few GDI/unknown tiny leaks, I could never tame these, and they did creep up over months of continuous use. Not worryingly, since at least once a year an update or server reset would restart the applications and reset associated memory, but enough to annoy me.

Following the ebb and flow of these creeps over time (via GetProcessMemoryInfo()), I came to the conclusion that Windows, perhaps not surprisingly, wasn’t doing a consistent cleanup job of resources it (as opposed to Delphi) allocated.

For me, relief came from half a dozen lines of code. Once a day, at some idle time, I now ask Windows to finish cleaning up properly, and the results are quite startling:

//-------------------------------
procedure TrimWorkingSet;
//-------------------------------
 //clear mem once a day, Windows lets it creep up over time
var
  hProcess: THandle;
begin
  hProcess := OpenProcess(PROCESS_SET_QUOTA, false, GetCurrentProcessId);
  try
   SetProcessWorkingSetSize(hProcess, $FFFFFFFF, $FFFFFFFF);
  finally
   CloseHandle(hProcess);
   Application.ProcessMessages;
  end;
end;

After this call, memory footprint is about the size it was on first startup, all stray GDI/unknowns creeps forgotten.

Would be interesting to see if this call (or a related one) also effects such a slimming cure on handles and other niggles you mention - haven’t checked, these were never a worry in my case.

1 Like

That’s interesting.

OK, I think I’ve tracked the issue down to where I’m using TCustomImageList.GetBitmap to update the Glyph and GlyphDisabled images of a tool button as the status of another process changes (paused vs not paused).

Should I be surrounding the two GetBitmap calls with something while it updates? At present I set the Glyph and GlyphDisabled to nil first, call GetBitmap for each then set the Enabled property last.

Can you show your code - since GetBitmap requires that you pass in a TBitmap instance - are you freeing that bitmap instance somewhere?

  BtPause.Glyph := nil;
  BtPause.GlyphDisabled := nil;
  if Paused then
  begin
    BtPause.Caption := 'Resume';
    ilTransport.GetBitmap(2, BtPause.Glyph);
    ilTransport.GetBitmap(3, BtPause.GlyphDisabled);
    BtPause.Enabled := True;
  end
  else
  begin
    BtPause.Caption := 'Pause';
    ilTransport.GetBitmap(0, BtPause.Glyph);
    ilTransport.GetBitmap(1, BtPause.GlyphDisabled);
    BtPause.Enabled := CanPause;
  end;

What component is BtPause? I would go and look at the code for the getter/setter on the Glyph property, what happens when set it to nil, and what happens when you read the property (which you are doing by passing as a parameter. I suspect the BtPause component is leaking bitmaps.

BtPause is a TMS TAdvToolButton. The Glyph setter contains a TBitmap.Assign operation. It also sets the disabled Glyph if it is not already set as part of the setter.

I’m clearing both Glyphs, setting the enabled one (which also ends up setting the disabled one), then I set the disabled one. Maybe on some systems there’s a race condition?

I might try only clearing each Glyph just before I set it again so that setting the enabled doesn’t affect the disabled one. The disabled one will be set after the enabled one (only) has been changed.

I might also keep track of the Glyph set required and only update them when actually required. At present it would be assigning them every time the associated grid row selection changes.

Clearing or freeing?

Clearing might be leaving ‘something’ behind, hopefully you can easily work out if this is causing the issue.

By clearing I meant setting to nil. That seems to be the correct way to do it going by the numerous examples I’ve seen. There doesn’t seem to be any increase in memory or objects in my testing. I’ve changed the code to only update the Glyphs when required now so we’ll see if that solves the issue of some users getting exceptions.

I don’t have access to that component, but if you have the source (you should) - have a look at the glyphi getter/setter - it’s possible it’s leaking.