System Error. Code: 8. Not enough storage is available to process this command
Asked Answered
A

7

39

We have a few Win32 applications (coded in Delphi 2006) where sometimes the user gets an error message saying "System Error. Code: 8. Not enough storage is available to process this command.".

From the stacktrace it looks like it is always during CreateWnd call

Main ($1edc):
004146cc +070 app.exe SysUtils               RaiseLastOSError
00414655 +005 app.exe SysUtils               RaiseLastOSError
004ce44c +130 app.exe Controls               TWinControl.CreateWnd
00535a72 +022 app.exe cxControls             TcxControl.CreateWnd
004ce82a +016 app.exe Controls               TWinControl.CreateHandle
00553d21 +005 app.exe cxContainer            TcxContainer.CreateHandle
00586ef1 +005 app.exe cxEdit                 TcxCustomEdit.CreateHandle
005c331d +005 app.exe cxDropDownEdit         TcxCustomDropDownEdit.CreateHandle
004ceaf0 +074 app.exe Controls               TWinControl.UpdateShowing
004ceb1e +0a2 app.exe Controls               TWinControl.UpdateShowing
004cebdc +03c app.exe Controls               TWinControl.UpdateControlState
004d118a +026 app.exe Controls               TWinControl.CMVisibleChanged
004cb713 +2bb app.exe Controls               TControl.WndProc
004cf569 +499 app.exe Controls               TWinControl.WndProc
004b727d +4c1 app.exe Forms                  TCustomForm.WndProc
004cb3a0 +024 app.exe Controls               TControl.Perform
004c9f6a +026 app.exe Controls               TControl.SetVisible
004b6c46 +03a app.exe Forms                  TCustomForm.SetVisible
004baf1b +007 app.exe Forms                  TCustomForm.Show
004bb151 +14d app.exe Forms                  TCustomForm.ShowModal
007869c7 +0d3 app.exe UfrmPrice      770 +19 TfrmPrice.EditPrice
0078655d +009 app.exe UfrmPrice      628  +0 TfrmPrice.actNewBidExecute
00431ce7 +00f app.exe Classes                TBasicAction.Execute
004c2cb5 +031 app.exe ActnList               TContainedAction.Execute
004c397c +050 app.exe ActnList               TCustomAction.Execute
00431bb3 +013 app.exe Classes                TBasicActionLink.Execute
004af384 +090 app.exe Menus                  TMenuItem.Click
004b059f +013 app.exe Menus                  TMenu.DispatchCommand
004b16fe +082 app.exe Menus                  TPopupList.WndProc
004b164d +01d app.exe Menus                  TPopupList.MainWndProc
004329a8 +014 app.exe Classes                StdWndProc
7e4196b2 +00a USER32.dll                     DispatchMessageA
004bea60 +0fc app.exe Forms                  TApplication.ProcessMessage
004bea9a +00a app.exe Forms                  TApplication.HandleMessage
004becba +096 app.exe Forms                  TApplication.Run
008482c5 +215 app.exe AppName        129 +42 initialization

I've never been able to get to the bottom of what causes this and as it happens fairly seldom I haven't been to concerned, but I would like to find out what causes it and hopefully rectify it...

EDIT: Full Stacktrace

EDIT 2: More info... The client who experienced this today has had my app installed for about 4 months and it is running on his PC 8 hours a day. The problem only appeared today and kept reappearing even though he killed my app and restarted it. None of the other apps on his system behaved strangely. After a reboot the problem goes away completely. Does this point towards the heap shortage that Steve mentions?

EDIT 3: Interesting msdn blog post here and here on the topic of the desktop heap. Though I'm not sure whether this is the cause of the problem it certainly looks likely.

Ardeen answered 3/2, 2009 at 16:32 Comment(0)
W
29

If your program uses a lot of windows resources it could be a Resource Heap shortage.

There is a registry entry that can be increased to raise the heap size for XP. For Vista Microsoft already sets the default value higher. I recommend changing the default 3072 to at least 8192.

This information is documented in the MS Knowledge Base (or search for "Out of Memory"). Additional details concerning the parameter values may be found in article KB184802.

I suggest you read the knowledgebase article but the basic info on the change is:

  1. Run Registry Editor (REGEDT32.EXE).

  2. From the HKEY_ LOCAL_MACHINE subtree, go to the following key:

    \System\CurrentControlSet\Control\Session Manager\SubSystem
    
  3. On the right hand side of the screen double-click on the key:

    windows 
    
  4. On the pop-up window you will see a very long field selected. Move the cursor near the beginning of the string looking for this (values may vary):

    SharedSection=1024,3072,512
    
  5. SharedSection specifies the System and desktop heaps using the following format: SharedSection=xxxx,yyyy,zzz where xxxx defines the maximum size of the system-wide heap (in kilobytes), yyyy defines the size of the per desktop heap, and zzz defines the size of the desktop heap for a "non-interactive" window station.

  6. Change ONLY the yyyy value to 8192 (or larger) and press OK.

  7. Exit the Registry Editor and reboot the PC for the change to take effect.

Good luck.

Wallasey answered 3/2, 2009 at 20:48 Comment(6)
Thanks. I'll try this with the client who experiences this the most. I've never been able to reproduce locally...Ardeen
Is there any way I can measure whether this heap has been depleted?Ardeen
Marius, how did you get the values to add to your bug reporting? from other answer: I thought so to so I added them to my bug reporting... USER handle limit: 10000 GDI handle limit: 10000 USER Handles in use: 562 GDI Handles in use: 520 – Marius (Feb 3 at 21:09)Wallasey
@Marius, measuring is not always as easy as checking the handle limits, but you can read about it here: #549471Kaiserslautern
@SteveBlack my sharedsection shows 20480 in yyyyy what value do i change it to?Encrinite
Is this fixed in Delphi 10?Camp
W
34

Actually this is a problem with ATOM table. I reported this issue to Embarcadero (saved in Wayback Machine) as it is causing me a lot of grieves.

If you monitor global atom table you will see that Delphi apps are leaking atoms, leaving the id of your app without dropping it off from memory:

You will see loads of following items:

**Delphi000003B4*

*Controlofs0040000000009C0**

Basically, since you can't register more than 0xFFFF different windows messages ID as soon as you ask for another one, the system will return "System Error. Code: 8. Not enough storage is available to process this command". Then you will not be able to start any app that creates a window.

Another issue (saved in Wayback Machine) was reported in Embarcadero QC Central.

This issue presents itself under Windows 7 / Windows Server 2008. The fact that on Windows Server 2003 and before it used to run is because of a wrong implementation, which recycles ATOMs once their index wrapped around the maximum of 16384 units.

Feel free to use my Global Atom Monitor to check whether your Delphi apps are leaking atoms or not.

To fix this you'll need a patch from Embarcadero, or download ControlsAtomFix1.7z from www.idefixpack.de/blog/downloads.

Whish answered 30/1, 2012 at 15:43 Comment(8)
Thank you, thank you, thank you. My only disappointment is that I didn't see this SO answer before today. Like Christian, I've spent (and wasted) countless hours trying to determine why our DataSnap application server was leaking resources. I couldn't even determine which kind of resource it was leaking. Although our application does not stop & start continuously, it does load and unload DLLs on demand multiple times a day. (It is unloaded when not in use primarily to allow the DLL to be easily updated without stopping the service.)Coprology
What is the error you get on the monitor? I haven't tried it on windows 10 if that's your OS.Whish
@JordiCorbilla I could really use the Global Atom Monitor tool - but I don't have a Delphi compiler, and there are no open source compilers. Any chance of getting a compiled exe added tot he github repo?Sylvia
@KevinDay it's in the release area -> github.com/JordiCorbilla/atom-table-monitor/releasesWhish
Have you installed the service?Whish
I don't get it. This bug was marked as fixed in Delphi XE2 and XE3. I have XE7 but still get the bug. What is Embarcadero doing there? qc.embarcadero.com/wc/qcmain.aspx?d=90511Camp
The link to the QC Central is broken (again).Camp
@Z80 I fixed those links and added the download link for ControlsAtomFix1.7z which contains the fix from Andreas Hausladen for Delphi 6 through and including XE. If you still see this bug, then your Delphi application is using a lot of registered Windows messages different from the one in Controls and needs adjusting. See devblogs.microsoft.com/oldnewthing/20150319-00/?p=44433 why.Vagus
W
29

If your program uses a lot of windows resources it could be a Resource Heap shortage.

There is a registry entry that can be increased to raise the heap size for XP. For Vista Microsoft already sets the default value higher. I recommend changing the default 3072 to at least 8192.

This information is documented in the MS Knowledge Base (or search for "Out of Memory"). Additional details concerning the parameter values may be found in article KB184802.

I suggest you read the knowledgebase article but the basic info on the change is:

  1. Run Registry Editor (REGEDT32.EXE).

  2. From the HKEY_ LOCAL_MACHINE subtree, go to the following key:

    \System\CurrentControlSet\Control\Session Manager\SubSystem
    
  3. On the right hand side of the screen double-click on the key:

    windows 
    
  4. On the pop-up window you will see a very long field selected. Move the cursor near the beginning of the string looking for this (values may vary):

    SharedSection=1024,3072,512
    
  5. SharedSection specifies the System and desktop heaps using the following format: SharedSection=xxxx,yyyy,zzz where xxxx defines the maximum size of the system-wide heap (in kilobytes), yyyy defines the size of the per desktop heap, and zzz defines the size of the desktop heap for a "non-interactive" window station.

  6. Change ONLY the yyyy value to 8192 (or larger) and press OK.

  7. Exit the Registry Editor and reboot the PC for the change to take effect.

Good luck.

Wallasey answered 3/2, 2009 at 20:48 Comment(6)
Thanks. I'll try this with the client who experiences this the most. I've never been able to reproduce locally...Ardeen
Is there any way I can measure whether this heap has been depleted?Ardeen
Marius, how did you get the values to add to your bug reporting? from other answer: I thought so to so I added them to my bug reporting... USER handle limit: 10000 GDI handle limit: 10000 USER Handles in use: 562 GDI Handles in use: 520 – Marius (Feb 3 at 21:09)Wallasey
@Marius, measuring is not always as easy as checking the handle limits, but you can read about it here: #549471Kaiserslautern
@SteveBlack my sharedsection shows 20480 in yyyyy what value do i change it to?Encrinite
Is this fixed in Delphi 10?Camp
C
26

I have been searching for 2 year and thanks to Jordi Corbilla answer I have finally got it!

In a few words: Delphi source has bugs that is causing you this problem!

Let's understand what is going on:

Windows has a memory area called "Atom table", wich serves to applications communicate each other (see more).

Also, Windows has another "memory area", called "Window Message System", wich serves to the same purpose (see more).

Both these memory areas have "16k slots" each. In the first one, it is possible to REMOVE an atom, by using the following Windows API:

GlobalDeleteAtom // for removing an atom added by "GlobalAddAtom"

In the second "area", we just CAN'T REMOVE anything!

The RegisterWindowMessage function is typically used to register messages for communicating between two cooperating applications. If two different applications register the same message string, the applications return the same message value. The message remains registered until the session ends.

Delphi compiled applications (by D7 at least) will put a record in "Messaging Area" and some others records in "Atom Table" EVERY TIME THEY ARE STARTED. The application tries to remove them when app is closing, but I have find many (and many) "atom leaks", even after app is closed.

At this point you can see that if you have a server that starts thousands of app a day, you probably should reach the 16k limit soon, and the problem begins! The solution at this point? Nothing but a single reboot.

So, what can we do? Well my friend, I'm sorry to tell you, but we need to FIX Delphi source code and recompile all applications.

First, open the unit Controls.pas and replace the following line:

RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));

for:

RM_GetObjectInstance := RegisterWindowMessage('RM_GetObjectInstance');

and then recompile Delphi packages and your applications.

As I have found atom leaks even after app is closed, I created an app that garbage collects any atom left behind. It just runs the following code every hour:

procedure GarbageCollectAtoms;
var i, len : integer;
    cstrAtomName: array [0 .. 1024] of char;
    AtomName, Value, procName: string;
    ProcID,lastError : cardinal;
    countDelphiProcs, countActiveProcs, countRemovedProcs, countCantRemoveProcs, countUnknownProcs : integer;

    // gets program's name from process' handle
    function getProcessFileName(Handle: THandle): string;
    begin
      Result := '';
      { not used anymore
      try
        SetLength(Result, MAX_PATH);
        if GetModuleFileNameEx(Handle, 0, PChar(Result), MAX_PATH) > 0 then
          SetLength(Result, StrLen(PChar(Result)))
        else
          Result := '';
        except
      end;
      }
    end;

    // gets the last 8 digits from the given atomname and try to convert them to and integer
    function getProcessIdFromAtomName(name:string):cardinal;
    var l : integer;
    begin
      result := 0;
      l := Length(name);
      if (l > 8) then
      begin
        try
          result := StrToInt64('$' + copy(name,l-7,8));
          except
            // Ops! That should be an integer, but it's not!
            // So this was no created by a 'delphi' application and we must return 0, indicating that we could not obtain the process id from atom name.
            result := 0;
        end;
      end;
    end;

    // checks if the given procID is running
    // results: -1: we could not get information about the process, so we can't determine if is active or not
    //           0: the process is not active
    //           1: the process is active
    function isProcessIdActive(id: cardinal; var processName: string):integer;
    var Handle_ID: THandle;
    begin
      result := -1;
      try
        Handle_ID := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, id);
        if (Handle_ID = 0) then
        begin
          result := 0;
        end
        else
        begin
          result := 1;
          // get program's name
          processName := getProcessFileName(Handle_ID);
          CloseHandle(Handle_ID);
        end;
        except
          result := -1;
      end;
    end;

    procedure Log(msg:string);
    begin
      // Memo1.Lines.Add(msg);
    end;


begin

  // initialize the counters
  countDelphiProcs := 0;
  countActiveProcs := 0;
  countRemovedProcs := 0;
  countUnknownProcs := 0;

  // register some log
  Log('');
  Log('');
  Log('Searching Global Atom Table...');

  for i := $C000 to $FFFF do
  begin
    len := GlobalGetAtomName(i, cstrAtomName, 1024);
    if len > 0 then
    begin
      AtomName := StrPas(cstrAtomName);
      SetLength(AtomName, len);
      Value := AtomName;
      // if the atom was created by a 'delphi application', it should start with some of strings below
      if (pos('Delphi',Value) = 1) or
         (pos('ControlOfs',Value) = 1) or
         (pos('WndProcPtr',Value) = 1) or
         (pos('DlgInstancePtr',Value) = 1) then 
      begin
        // extract the process id that created the atom (the ProcID are the last 8 digits from atomname)
        ProcID := getProcessIdFromAtomName(value);
        if (ProcId > 0) then
        begin
          // that's a delphi process
          inc(countDelphiProcs);
          // register some log
          Log('');
          Log('AtomName: ' + value + ' - ProcID: ' + inttostr(ProcId) + ' - Atom Nº: ' + inttostr(i));
          case (isProcessIdActive(ProcID, procName)) of
            0: // process is not active
            begin
              // remove atom from atom table
              SetLastError(ERROR_SUCCESS);
              GlobalDeleteAtom(i);
              lastError := GetLastError();
              if lastError = ERROR_SUCCESS then
              begin
                // ok, the atom was removed with sucess
                inc(countRemovedProcs);
                // register some log
                Log('- LEAK! Atom was removed from Global Atom Table because ProcID is not active anymore!');
              end
              else
              begin
                // ops, the atom could not be removed
                inc(countCantRemoveProcs);
                // register some log
                Log('- Atom was not removed from Global Atom Table because function "GlobalDeleteAtom" has failed! Reason: ' + SysErrorMessage(lastError));
              end;
            end;
            1: // process is active
            begin
              inc(countActiveProcs);
              // register some log
              Log('- Process is active! Program: ' + procName);
            end;
            -1: // could not get information about process
            begin
              inc(countUnknownProcs);
              // register some log
              Log('- Could not get information about the process and the Atom will not be removed!');
            end;
          end;
        end;
      end;
    end;
  end;
  Log('');
  Log('Scan complete:');
  Log('- Delphi Processes: ' + IntTostr(countDelphiProcs) );
  Log('  - Active: ' + IntTostr(countActiveProcs) );
  Log('  - Removed: ' + IntTostr(countRemovedProcs) );
  Log('  - Not Removed: ' + IntTostr(countCantRemoveProcs) );
  Log('  - Unknown: ' + IntTostr(countUnknownProcs) );

  TotalAtomsRemovidos := TotalAtomsRemovidos + countRemovedProcs;

end;

(This code above was based on this code)

After that, I have never got this f** error again!

Late Update:

Also, that is the source of this error: Application error: fault address 0x00012afb

Charybdis answered 12/5, 2012 at 2:48 Comment(8)
Thanks Christian. I'll put this into our next release and see if it helps. Thanks for sharingArdeen
one question to make sure I understand your explanation: this "memory leak" appears by simply running applications (any application) or only Delphi applications?Camp
I confirmed 'til Delphi 7 (pretty old...) Probably they already fixed (or not?). Never saw it happening with other applications than those built by Delphi.Charybdis
@Charybdis - but I have this bug in Delphi XE7!Camp
@Charybdis - procName is not initialized!Camp
Update: If the library is used under Delphi Rio (10.3.3) it will cause weird stuff (for example double cursor in Obj Inspector) into the IDE if the IDE is minimized.Camp
For me the processes rundll32.exe ("ControlOfs004000000000124C", "WndProcPtr004000000000124C"), Vivaldi v3.0.1874.32 ("Delphi0000305C"), DU Meter v3.02 ("Delphi000008C4") and No-IP DUC v2.2.0 ("Delphi00000214") are identified. The former cannot be compiled by Delphi - I guess this is just some coincidence. I'm mildly suprised by Vivaldi.Confide
An in-memory fix .PAS unit to include into your projects can be found at Andreas Hausladen: ControlsAtomFix1.7z.Confide
K
2

You can use Desktop Heap Monitor from Microsoft to view heap statistics (use % etc.) and is available at:

http://www.microsoft.com/downloads/details.aspx?familyid=5cfc9b74-97aa-4510-b4b9-b2dc98c8ed8b&displaylang=en

Kannada answered 13/11, 2009 at 17:43 Comment(0)
T
2

I noticed this error (System Error. Code: 8. Not enough storage...) recently when using some Twain code, it was happening on my computer and not my coworker's, and the only real difference between our machines is that I use the laptop screen as a second monitor, so my total desktop dimensions are larger.

I found documentation of the problem pointed to above by Steve Black, but I found a way (that fixed the error on my machine, at least) that does not require editing the registry:

The old code was using

  DC := GetDC(Owner.VirtualWindow);
  // ...
  ReleaseDC(Owner.VirtualWindow, DC);

and I found that replacing it with this got rid of my errors

  DC := CreateCompatibleDC(Owner.VirtualWindow);
  // ...
  DeleteDC(DC);


I don't know if this has relevance to your problem, but it may be helpful for others in the future.

Thrombophlebitis answered 13/11, 2009 at 21:35 Comment(0)
I
1

There could be bugs in the compiler, it's a fair bet it's something in your app that is causing the problem. Could it be that your app is leaking window handles or some other GUI object like pens/brushes? This could be a cause.

Idolism answered 3/2, 2009 at 18:1 Comment(0)
S
0

To me it was just a bunch of TJPEGImages decompressing in a slide-show manner that eventually ran out of memory.

Splice answered 25/1, 2018 at 11:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.