Системная ошибка. Код: 8. Недостаточно памяти для обработки этой команды
У нас есть несколько приложений 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
Мне никогда не удавалось разобраться в причинах этого, и, поскольку это случается довольно редко, меня это не беспокоило, но я хотел бы выяснить, что вызывает это, и, надеюсь, исправить это...
РЕДАКТИРОВАТЬ: Full Stacktrace
РЕДАКТИРОВАТЬ 2: Подробнее... Клиент, который испытал это сегодня, установил мое приложение в течение примерно 4 месяцев, и оно работает на своем компьютере 8 часов в день. Проблема появилась только сегодня и продолжала появляться, даже если он убил мое приложение и перезапустил его. Ни одно из других приложений в его системе не вело себя странно. После перезагрузки проблема полностью исчезает. Указывает ли это на нехватку кучи, о которой упоминает Стив?
РЕДАКТИРОВАТЬ 3: Интересное сообщение в блоге msdn здесь и здесь на тему кучи рабочего стола. Хотя я не уверен, является ли это причиной проблемы, это, безусловно, выглядит вероятным.
7 ответов
Если ваша программа использует много ресурсов 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 (или больше) и нажмите ОК.Выйдите из редактора реестра и перезагрузите компьютер, чтобы изменения вступили в силу.
Удачи.
На самом деле это проблема таблицы ATOM. Я сообщил об этой проблеме Embarcadero, так как она вызывает у меня много горя.
Если вы наблюдаете за таблицей глобальных атомов, вы увидите, что в приложениях Delphi происходят утечки атомов, а идентификатор вашего приложения остается без удаления из памяти:
Вы увидите множество следующих предметов:
**Delphi000003B4*
*Controlofs0040000000009C0**
По сути, поскольку вы не можете зарегистрировать более 0xFFFF различных идентификаторов сообщений Windows, как только вы попросите другое, система вернет сообщение "Системная ошибка. Код: 8. Недостаточно памяти для обработки этой команды". Тогда вы не сможете запустить любое приложение, которое создает окно.
Другая проблема была зарегистрирована в Embarcadero QC Central.
Эта проблема возникает в Windows 7 / Windows Server 2008. Тот факт, что в Windows Server 2003 и до его запуска выполнялась из-за неправильной реализации, которая перезагружает ATOM, когда их индекс оборачивается максимум до 16384 единиц.
Не стесняйтесь использовать мой Global Atom Monitor, чтобы проверить, имеют ли ваши приложения Delphi утечки атомов или нет.
Чтобы это исправить, вам понадобится патч от Embarcadero.
Я искал 2 года, и благодаря ответу Джорди Корбиллы я наконец-то получил его!
В двух словах: в источнике Delphi есть ошибки, которые вызывают эту проблему!
Давайте разберемся, что происходит:
Windows имеет область памяти под названием "Таблица Atom", которая служит для того, чтобы приложения взаимодействовали друг с другом ( см. Подробнее).
Кроме того, в Windows есть еще одна "область памяти", называемая "Система сообщений окон", которая служит той же цели ( см. Подробнее).
Обе эти области памяти имеют по 16 тыс. Слотов. В первом из них можно УДАЛИТЬ атом, используя следующий Windows API:
GlobalDeleteAtom // for removing an atom added by "GlobalAddAtom"
Во второй "зоне" мы просто НЕ МОЖЕМ УДАЛИТЬ!
Функция RegisterWindowMessage обычно используется для регистрации сообщений для связи между двумя взаимодействующими приложениями. Если два разных приложения регистрируют одну и ту же строку сообщения, приложения возвращают одно и то же значение сообщения. Сообщение остается зарегистрированным до окончания сеанса.
Скомпилированные Delphi приложения (по крайней мере, D7) будут помещать записи в "область сообщений" и некоторые другие записи в "таблицу атомов " КАЖДЫЙ ВРЕМЯ, КОТОРЫЕ ОНИ НАЧАЛАСЬ. Приложение пытается удалить их, когда приложение закрывается, но я обнаружил много (и много) "утечек атома", даже после закрытия приложения.
На этом этапе вы можете видеть, что если у вас есть сервер, который запускает тысячи приложений в день, вы, вероятно, скоро достигнете лимита в 16 тыс., И проблема начнется! Решение на данный момент? Ничего, кроме одной перезагрузки.
Так что мы можем сделать? Что ж, друг мой, извините, что говорю вам, но нам нужно исправить исходный код 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
Вы можете использовать Desktop Heap Monitor от Microsoft для просмотра статистики кучи (используйте% и т. Д.) И доступен по адресу:
Я недавно заметил эту ошибку (Системная ошибка. Код: 8. Недостаточно памяти...) при использовании некоторого кода Twain, это происходило на моем компьютере, а не на моем коллеге, и единственное реальное различие между нашими машинами заключается в том, что я использую Экран ноутбука как второй монитор, поэтому у меня общие размеры рабочего стола больше.
Я нашел документацию по проблеме, указанной выше Стивом Блэком, но я нашел способ (по крайней мере, исправивший ошибку на моем компьютере), который не требует редактирования реестра:
Старый код использовался
DC := GetDC(Owner.VirtualWindow);
// ...
ReleaseDC(Owner.VirtualWindow, DC);
и я обнаружил, что замена этого избавила меня от моих ошибок
DC := CreateCompatibleDC(Owner.VirtualWindow);
// ...
DeleteDC(DC);
Я не знаю, имеет ли это отношение к вашей проблеме, но это может быть полезно для других в будущем.
В компиляторе могут быть ошибки, справедливо поспорить, что проблема в вашем приложении. Может ли быть так, что в вашем приложении просочились дескрипторы окон или другие объекты GUI, такие как ручки / кисти? Это может быть причиной.
Для меня это была просто распаковка TJPEGImages в режиме слайд-шоу, которая в итоге исчерпала память.