当前位置:  开发笔记 > 运维 > 正文

系统错误.代码:8.没有足够的存储空间来处理此命令

如何解决《系统错误.代码:8.没有足够的存储空间来处理此命令》经验,为你挑选了3个好方法。

我们有一些Win32应用程序(在Delphi 2006中编码),有时用户会收到一条错误消息"系统错误.代码:8.没有足够的存储空间来处理此命令"..

从堆栈跟踪看起来它总是在CreateWnd调用期间

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

我从来没有能够找到导致这种情况的根源,因为它很少发生我很少关注,但我想找出导致它的原因并希望纠正它...

编辑:完整Stacktrace

编辑2:更多信息...今天遇到这个问题的客户已经安装了我的应用程序大约4个月,它每天8小时都在他的电脑上运行.问题只出现在今天并且一直重复出现,即使他杀了我的应用程序并重新启动它.他的系统中没有其他应用程序表现得很奇怪.重启后问题完全消失了.这是否指向史蒂夫提到的堆短缺?

编辑3:有趣的msdn博客文章在这里和这里关于桌面堆的主题.虽然我不确定这是否是问题的原因,但它看起来很可能.



1> 小智..:

实际上这是ATOM表的一个问题.我向Embarcadero报告了这个问题,因为它给我带来了很多悲伤.

如果您监视全局原子表,您将看到Delphi应用程序正在泄漏原子,留下您的应用程序的ID而不会从内存中删除它:

您将看到大量以下项目:

**Delphi000003B4*

*Controlofs0040000000009C0**

基本上,由于您在请求另一个时不能注册超过0xFFFF的不同Windows消息ID,系统将返回" 系统错误.代码:8.没有足够的存储空间来处理此命令 ".然后,您将无法启动任何创建窗口的应用程序.

Embarcadero QC Central报道了另一个问题.

此问题出现在Windows 7/Windows Server 2008下.事实上,在Windows Server 2003上以及它之前运行是因为错误的实现,一旦索引包含最多16384个单元,它就会回收ATOM.

随意使用我的全局原子监视器检查您的Delphi应用程序是否正在泄漏原子.

要解决此问题,您需要Embarcadero的补丁.


谢谢你,谢谢你,谢谢你.我唯一的失望是在今天之前我没有看到这个答案.像Christian一样,我花了很多时间(并浪费时间)试图确定我们的DataSnap应用服务器泄漏资源的原因.我甚至无法确定它泄漏了哪种资源.虽然我们的应用程序不会持续停止和启动,但它确实每天多次加载和卸载DLL.(它在不使用时被卸载主要是为了在不停止服务的情况下轻松更新DLL.)

2> 小智..:

如果您的程序使用大量Windows资源,则可能是资源堆短缺.

有一个注册表项可以增加,以提高XP的堆大小.对于Vista,Microsoft已将默认值设置得更高.我建议将默认值3072更改为至少8192.

此信息记录在MS知识库中 (或搜索"内存不足").有关参数值的其他详细信息,请参见文章KB184802.

我建议您阅读知识库文章,但有关更改的基本信息是:

    运行注册表编辑器(REGEDT32.EXE).

    从HKEY_ LOCAL_MACHINE子树,转到以下键:

    \System\CurrentControlSet\Control\Session Manager\SubSystem
    

    在屏幕的右侧双击键:

    windows 
    

    在弹出窗口中,您将看到一个非常长的字段.将光标移动到字符串开头附近寻找此值(值可能会有所不同):

    SharedSection=1024,3072,512
    

    SharedSection使用以下格式指定系统和桌面堆:SharedSection=xxxx,yyyy,zzz其中,xxxx定义系统范围堆的最大大小(以千字节为单位),yyyy定义每个桌面堆zzz的大小,并为"非"定义桌面堆的大小.互动"窗口站.

    仅将yyyy值更改为8192(或更大),然后按确定.

    退出注册表编辑器并重新启动PC以使更改生效.

祝好运.


有什么方法可以衡量这个堆是否已经耗尽?
@SteveBlack我的共享部分显示20480 yyyyy我将它更改为什么值?

3> Christian..:

我一直在寻找2年,感谢Jordi Corbilla回答我终于明白了!

简而言之:Delphi源码存在导致此问题的错误!

让我们了解发生了什么:

Windows有一个名为"Atom table"的内存区域,它可以让应用程序相互通信(参见更多信息).

此外,Windows还有另一个"内存区域",称为"窗口消息系统",它具有相同的用途(参见更多信息).

这两个存储区域各有"16k插槽".在第一个中,可以使用以下Windows API 删除原子:

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

在第二个"区域",我们无法删除任何东西!

RegisterWindowMessage函数通常用于注册消息以在两个协作应用程序之间进行通信.如果两个不同的应用程序注册相同的消息字符串,则应用程序返回相同的消息值.消息将保持注册,直到会话结束.

德尔福编译的应用程序(由D7至少)将投入"信息区"的记录和其他一些记录"原子表" 每当他们启动.应用程序尝试在应用程序关闭时删除它们,但我发现很多(和很多)"原子泄漏",即使应用程序关闭后也是如此.

此时您可以看到,如果您的服务器每天启动数千个应用程序,您可能很快就会达到16k限制,问题就开始了!此时的解决方案?只需一次重启就可以了.

所以,我们能做些什么?好吧,我的朋友,很抱歉告诉你,但我们需要修复Delphi源代码并重新编译所有应用程序.

首先,打开Controls.pas单元并替换以下行:

RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));

对于:

RM_GetObjectInstance := RegisterWindowMessage('RM_GetObjectInstance');

然后重新编译Delphi包和您的应用程序.

因为我发现即使应用程序关闭后原子泄漏,我创建了一个垃圾收集任何原子留下的应用程序.它每小时运行以下代码:

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;

(以上代码基于此代码)

在那之后,我再也没有得到这个f**错误!

晚更新:

此外,这是此错误的来源:应用程序错误:故障地址0x00012afb

推荐阅读
kikokikolove
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有