Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Compatibility/Operating System    [ Add a report in this area ]  
Report #:  73347   Status: Closed
Memory not really returned to the OS (Win2008) after using CriticalSections
Project:  Delphi Build #:  all
Version:    12.1 Submitted By:   Francois GAILLARD
Report Type:  Crash / Data loss / Total failure Date Reported:  4/24/2009 7:11:58 PM
Severity:    Serious / Highly visible problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All platforms Internal Tracking #:   269188
Resolution: Cannot Fix (Resolution Comments) Resolved in Build: : 14.0.3491.23395
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
*** Windows Server 2008 ***

When running under Windows Server 2008 (both x32 and x64), a Delphi (2007 or 2009) application does not return fully the memory to the OS. Especially, as demonstrated by the sample code, when using Interfaced Objects that are auto-freed by reference counting.
The Memory manager reports the memory as being freed normally (TMemoryManagerState).

It behaves as expected on XP, XP pro, Windows Server 2003.

*** UPDATE ***
After further investigation, it appears linked to using Critical Sections in Vista/Win2008.
It was happening in the sample code because TInterfaceList contains a TThreadList that uses Critical Sections.
I provide a new sample project (pasted in Steps) showing this behavior with Critical Sections only.
Steps to Reproduce:
*** Windows Server 2008 ***

Build a sample console application with the code below.
Monitor the application memory with Sysinternals Process Explorer.
The Private Bytes count in particular do not go down after execution to where  it was.
Even if the memory pressure increases, the "lost" memory does not return to the OS until the application is terminated.

If you do this multiple times with multiple instances, the machine eventually runs out of physical memory and stops working.

program PTestMemoryCS;


  SysUtils, Windows;

  CS_NUMBER = 10000000;
  TCSArray = Array[1..CS_NUMBER] of TRTLCriticalSection;
  PCSArray = ^TCSArray;

procedure TestStatic;
  csArray: PCSArray;
  idx: Integer;

  for idx := 1 to length(csArray^) do

  for idx := 1 to length(csArray^) do


procedure TestDynamic(const Number: Integer);
  csArray: array of TRTLCriticalSection;
  idx: Integer;
  SetLength(csArray, Number);

  for idx := Low(csArray) to High(csArray) do

  for idx := Low(csArray) to High(csArray) do

  ReportMemoryLeaksOnShutdown := True;
  Write('press a key.');
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);


Tomohiro Takahashi at 4/24/2009 7:54:56 PM -
Please attach sample project to reproduce your issue on Windows 2008.

Francois GAILLARD at 4/29/2009 3:48:47 PM -
Please read the Update in description and see the sample console program in steps.
It's definitely linked to Critical Sections.

Francois GAILLARD at 4/25/2009 9:42:18 PM -
Done. But not really much more than the code pasted with the steps...

Tomohiro Takahashi at 4/27/2009 1:16:52 AM -
However, I can not reproduce your issue.

> If you do this multiple times with multiple instances ...
How many times and how many instances?

Francois GAILLARD at 4/27/2009 1:20:21 PM -
I have added 3 screenshots of the Process Explorer pane showing the memory footprint for the sample app:
- Before.png shows the Private Bytes at 2.6MB, just after starting the app and before clicking on the button to run the test.
- Running1.png shows the memory going up and down when running the test (1st run).
- After1run.png shows that the Private Bytes did NOT go down after finishing the 1st run to where it was before: it stays at 120MB.

120 MB is significantly less than the peak of 1.5 GB, but also significantly more than the starting point of 2.6 MB.  Additionally if you click the button again to run the test a 2nd time, it comes down only to 181MB after running.
... and so on, increasing the amount of Physical Memory kept after each run.

On the contrary, on XP or Win2003, the Private Bytes count comes down to about 4MB after the 1st test run, and comes back consistently to this value after every subsequent test run, effectively releasing its memory.

Now, depending on your machine's memory and CPU, the number of instances that you can run will vary.

Suppose you have launched 10 of them on a 4GB machine. They should hold a mere 40MB total when all idle, allowing 2 instances to run the test at the same time. But if they have been running the test enough times, each of them can hold up to 300+MB, bringing the total available physical memory down to near ZERO.

Also worth noting: this does not happen with Objects (for instance a TObjectList containing TObjectLists, normally freed).

Alex Alexeev at 5/22/2009 2:06:14 AM -
It appears that CS behavior changed on Vista/Win2008:

It is not Delphi's issue.

Tomohiro Takahashi at 5/23/2009 6:00:57 AM -
Thanks for the update.

Server Response from: ETNACODE01