Системная ошибка. Код: 8. Недостаточно памяти для обработки этой команды

36

У нас есть несколько приложений Win32 (закодированных в Delphi 2006), где иногда пользователь получает сообщение об ошибке "Системная ошибка". Код: 8. Недостаточно памяти для обработки этой команды..

Из stacktrace это похоже на то, что он всегда во время вызова 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

Я никогда не мог понять, что вызывает это, и, поскольку это происходит довольно редко, я не был заинтересован, но я хотел бы узнать, что вызывает его и, надеюсь, исправить его...

EDIT: Полная Stacktrace

РЕДАКТИРОВАТЬ 2: Подробнее... Клиенту, который испытал это сегодня, было установлено мое приложение около 4 месяцев, и он работает на его ПК 8 часов в день. Проблема появилась только сегодня и продолжала появляться, даже если он убил мое приложение и перезапустил его. Ни одно из других приложений в его системе не вел себя странно. После перезагрузки проблема полностью исчезает. Это указывает на нехватку кучи, о которой говорит Стив?

РЕДАКТИРОВАТЬ 3: Интересное сообщение в блоге msdn здесь и здесь по теме кучи рабочего стола. Хотя я не уверен, является ли это причиной проблемы, это, безусловно, выглядит вероятным.

Теги:
winapi

7 ответов

25
Лучший ответ

Если ваша программа использует много ресурсов 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 определяет кучи System и desktop в следующем формате: SharedSection=xxxx,yyyy,zzz где xxxx определяет максимальный размер общесистемной кучи (в килобайтах), yyyy определяет размер рабочего стола куча и zzz определяет размер рабочей области для "неинтерактивной" оконной станции.

  • Измените ТОЛЬКО значение yyyy на 8192 (или больше) и нажмите OK.

  • Закройте редактор реестра и перезагрузите компьютер, чтобы изменения вступили в силу.

Удачи.

  • 0
    Благодарю. Я попробую это с клиентом, который испытывает это больше всего. Я никогда не был в состоянии воспроизвести локально ...
  • 4
    Можно ли как-то измерить, исчерпана ли эта куча?
Показать ещё 3 комментария
31

На самом деле это проблема с таблицей ATOM. Я сообщил об этой проблеме Embarcadero, поскольку это вызывает у меня много огорчений.

Если вы будете наблюдать за глобальной таблицей атомов, вы увидите, что приложения Delphi просачивают атомы, оставляя id вашего приложения, не отбрасывая его из памяти:

Вы увидите множество следующих элементов:

**Delphi000003B4*

*Controlofs0040000000009C0**

В принципе, поскольку вы не можете зарегистрировать больше, чем 0xFFFF, идентификационный код Windows, как только вы попросите другого, система вернет " Системная ошибка. Код: 8. Недостаточно памяти для обработки этого команда". Тогда вы не сможете запустить любое приложение, создающее окно.

Еще одна проблема была опубликована в EmCardadero QC Central.

Эта проблема возникает под Windows 7/Windows Server 2008. Тот факт, что в Windows Server 2003 и до того, как он использовался для запуска, вызван неправильной реализацией, которая перерабатывает ATOM после того, как их индекс обернут максимум 16384 единиц.

Не стесняйтесь использовать мой Global Atom Monitor, чтобы проверить, не запутались ли ваши приложения Delphi или нет.

Чтобы исправить это, вам понадобится патч от Embarcadero.

  • 2
    Спасибо Спасибо спасибо. Мое единственное разочарование в том, что я не видел этот ответ до сегодняшнего дня. Как и Кристиан, я потратил (и потратил) бесчисленные часы, пытаясь выяснить, почему на нашем сервере приложений DataSnap произошла утечка ресурсов. Я даже не мог определить, какой ресурс это утечка. Хотя наше приложение не останавливается и не запускается постоянно, оно загружает и выгружает библиотеки DLL по требованию несколько раз в день. (Он выгружается, когда не используется, в первую очередь, чтобы можно было легко обновлять DLL без остановки службы.)
  • 0
    Ссылка на QC Central не работает. Embarcadero испугался, как много сообщений об ошибках, и удалил старый список контроля качества.
Показать ещё 6 комментариев
22

Я искал 2 года, и благодаря ответу Джорди Корбиллы я наконец получил его!

В нескольких словах: у источника Delphi есть ошибки, которые вызывают у вас эту проблему!

Дайте понять, что происходит:

В Windows есть область памяти, называемая "Таблица Atom", которая служит приложениям, взаимодействующим друг с другом (см. Больше).

Кроме того, Windows имеет еще одну "область памяти", называемую "Window Message System", которая служит для той же цели (см. Больше).

Обе области памяти имеют слоты "16k". В первом из них можно удалить REMOVE, используя следующий API Windows:

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

Во второй "области" мы просто НЕ МОЖЕМ УДАЛИТЬ ВСЕ !

Функция RegisterWindowMessage обычно используется для регистрации сообщений для связи между двумя взаимодействующими приложениями. Если два разных приложения регистрируют одну и ту же строку сообщений, приложения возвращают одно и то же значение сообщения. Сообщение остается зарегистрированным до окончания сеанса.

Скомпилированные приложения Delphi (по крайней мере, по D7) будут помещать запись в "область обмена сообщениями", а некоторые другие записи в "Таблице Atom" КАЖДЫЙ РАЗ ОНИ НАЧАЛСЯ. Приложение пытается удалить их, когда приложение закрывается, но я обнаружил много (и многих) "утечек атомов" даже после закрытия приложения.

На этом этапе вы можете увидеть, что если у вас есть сервер, который запускает тысячи приложений в день, вы, вероятно, должны скоро достигнуть предела 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 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 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 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 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

  • 1
    Спасибо, Кристиан. Я добавлю это в наш следующий выпуск и посмотрю, поможет ли это. Спасибо, что поделился
  • 0
    один вопрос, чтобы убедиться, что я понимаю ваше объяснение: эта «утечка памяти» возникает при простом запуске приложений (любого приложения) или только приложений Delphi?
Показать ещё 4 комментария
2

Я заметил эту ошибку (System Error. Code: 8. Недостаточно памяти...) в последнее время при использовании некоторого кода Twain, это происходило на моем компьютере, а не на моем коллеге, и единственное реальное различие между нашими машинами заключается в том, что Я использую экран ноутбука как второй монитор, поэтому мои общие размеры рабочего стола больше.

Я нашел документацию о проблеме, указанную выше Стивом Блэком, но я нашел способ (который исправил ошибку на моей машине, по крайней мере), которая не требует редактирования реестра:

Старый код использовал

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

и я обнаружил, что замена его на это избавилась от моих ошибок

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


Я не знаю, имеет ли это отношение к вашей проблеме, но может быть полезно для других в будущем.

2

Вы можете использовать Desktop Heap Monitor от Microsoft для просмотра статистики кучи (используйте% и т.д.) и доступны по адресу:

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

0

Для меня это была всего лишь куча TJPEGImages, декомпрессирующаяся в стиле слайд-шоу, которая в конечном итоге закончилась нехваткой памяти.

0

В компиляторе могут быть ошибки, это справедливая ставка, что-то в вашем приложении, которое вызывает проблему. Может ли быть, что ваше приложение пропускает дескрипторы окон или какой-либо другой объект графического интерфейса, например, ручки/кисти? Это может быть причиной.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню