Охота на EOutOfResources

Вопрос:

Есть ли простой способ получить список типов ресурсов, которые протекают в работающем приложении? IOW, подключившись к приложению?

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

Это не проблема, что сама проверка является катастрофической (останавливает процесс приложения), так как я могу проверить с помощью taskmgr, приближаюсь ли я (или, по крайней мере, я надеюсь)

Также приветствуются любые другие идеи по поиску утечек ресурсов (но не памяти).

Задний план:

У меня есть приложение Delphi 7/2006/2009 (компилируется со всеми тремя), и примерно через несколько недель оно начинает вести себя забавно. Однако только в одном месте он работает, в нескольких других системах он работает до отключения электроэнергии.

Я попытался добавить отладочный код, чтобы сузить проблему. и обнаружил, что исключение составляет EOutofResources при сохранении файла. (сохранение файла может происходить тысячи раз в день).

Я пытался объяснить утечки памяти (с помощью fastmm), но поскольку поток данных довольно велик (60 МБ / с с гигабитных промышленных камер), я могу исключить только "ползучие" утечки памяти с помощью fastmm, а не быстрые вспышки утечек памяти, которые истощают воспоминания о времени, когда это происходит. Если что-то пойдет не так, приложение заполнит память менее чем за полминуты.,

Основными подозреваемыми являются дескрипторы файлов, которые каким-то образом остаются при какой-либо ошибке, и TMetafiles (которые передаются в эти файлы). Незначительные подозреваемые - VST, всплывающие меню и фреймы.

Обновления:

Другой возможный совет: два года он работал с D7 нормально, а теперь проблемы с Turbo Explorer (который я использую для стабильных проектов, не преобразованных в D2009).

Пол-Ян: Поскольку это происходит только раз в неделю (а это может происходить ночью), сбор информации идет медленно. Вот почему я задаю этот вопрос: нужно комбинировать вещи, когда я буду там в четверг. Короче: нет, я не уверен на 100%. Я собираюсь принести всю коллекцию Systemtools, чтобы посмотреть, смогу ли я что-нибудь найти (потому что тогда она будет работать в течение нескольких дней). Также есть вероятность, что я вижу открытые файлы. (может быть, стоит попытаться найти немного mingw lsof и запланировать его)

Но приложение видит очень мало действий графического интерфейса (это приложение для проверки машинного зрения), за исключением обновления экрана +/- 15 / с, которое является tbitmap stretchdraw + tmetafile, но я получаю эту ошибку при сохранении на диск (TFileStream), дескрипторы, вероятно, < я> действительно измучен. Однако в том же потоке TMetafile также сохраняется в потоке, чего больше нет в более поздних приложениях, и они могут работать в течение нескольких месяцев.

------------------- ОБНОВИТЬ

Я искал, искал и искал, и мне удалось воспроизвести проблемы in-vitro два или три раза. Проблемы возникали, когда память была +/- 256 МБ (в системе 2 ГБ), пользовательские объекты 200, объекты gdi 500, ни один файл не был открыт больше, чем ожидалось).

На самом деле это не исключение. Я действительно замечаю, что у меня утечка небольшого количества дескрипторов, вероятно, из-за репарентинга фреймов (что-то в VCL, похоже, пропускает HPalette), но я подозреваю, что основная причина - другая проблема. Я повторно использую TMetafile и очищаю его между собой. Я думаю, что очистка метафайла на самом деле (всегда?) Не изменяет размер ресурса, в конечном итоге каждый метафайл во всем пуле tmetafile имеет максимальный размер, а с 20-40 + tmetafile (которые могут быть несколько 100k каждый) это попадет на рабочий стол ограничение кучи.

Это теория, но я попытаюсь проверить это, установив ограничение рабочего стола на 10 МБ у клиентов, но пройдет несколько недель, прежде чем я получу подтверждение, изменится ли это что-нибудь. Эта теория также подтверждает, почему эта машина является особенной (возможно, что у этой машины в среднем немного большие метафайлы). Иногда также может помочь освобождение и воссоздание t-метафайла в пуле.

К счастью, все эти проблемы (как tmetafile, так и повторное родительство) уже были разработаны в новых поколениях приложений.

Из-за особых обстоятельств (и того факта, что у меня очень ограниченные тестовые окна) это займет некоторое время, но я решил пока взять кучу рабочего стола в качестве примера (хотя материал GDILeaks также был несколько полезен).

Другое дело, что аудит выявил использование GDI-типов в потоке (правда, с сохранением только tmetafiles (которые не использовались или не подключались иным образом) в потоках.

------------- Обновление 2.

Увеличение лимита рабочего стола лишь незначительно увеличило время до возникновения проблемы.

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

Подводя итог, я могу только указать, какие три основные модификации произошли от старого к новому фреймворку:

  • Я больше не меняю экраны, переделывая фреймы. Теперь я работаю с формами, которые скрываю и показываю. Я изменил это, так как из-за этого у меня также были очень редкие сбои или исключения (которые можно было удалить). Сбои происходили во время работы с графическим интерфейсом, а не спонтанно, как основная проблема.
  • Процедура, в которой произошел сбой, связана с TMetafile. TMetafile был разработан и заменен более простым собственным форматом. (в основном массивы с вершинами Opengl)
  • Рисование больше не происходило с tbitmap с наложенным поверх него tmetafile оверлеем, а с использованием OpenGL.

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

Обновлено ноя 2012 после обсуждения в частной переписке: в ретроспективе следующим шагом было бы добавление счетчика к объектам метафайлов и их простое повторное создание каждые x * 1000 использований или около того, и посмотреть, изменится ли это. что-либо. Если у вас есть подобные проблемы, попробуйте посмотреть, можете ли вы как-то регулярно уничтожать и повторно инициализировать долгоживущие ресурсы, которые распределяются динамически.


person Marco van de Voort    schedule 01.02.2010    source источник
comment
Хотя диспетчер задач не может показать вам тип протекающих дескрипторов, он может помочь подтвердить, есть ли вообще протекающие дескрипторы. Мне неясно, так ли это, возможно, вы можете обновить свой ответ этой информацией?   -  person Paul-Jan    schedule 02.02.2010
comment
У меня сейчас почти такая же проблема, но здесь нет метафайлов.   -  person Warren P    schedule 23.07.2010
comment
Как изменить ограничения рабочего стола?   -  person Warren P    schedule 23.07.2010
comment
Эта же ошибка уже давно меня преследует.   -  person Z80    schedule 30.10.2011
comment
Алтарь: Оглядываясь назад, я подозреваю TMetafile или TBitmap. Чтобы показать им, они были нарисованы на панелях, а затем, при желании, были сохранены. Я подозреваю, что там что-то не так. Я перешел на opengl, и единственная проблема - болезненная поддержка шрифтов.   -  person Marco van de Voort    schedule 30.10.2011
comment
Я просто добавил ответ, описывающий, как я решил эту проблему, не раскрашивая напрямую на холст основной формы, который также содержал другие элементы управления. Проблема не возникла, так как я сделал все рисование на собственном холсте, а не на холсте основной формы.   -  person Jerry Dodge    schedule 23.09.2019


Ответы (8)


Если это утечки дескрипторов GDI, вы можете взглянуть на Журнал MSDN, январь 2003 г., в котором используется инструмент GDILeaks. Другие инструменты: GDIObj или GDIView. См. Также здесь .

Другой источник EOutOfResources может заключаться в том, что Desktop Heap заполнен. У меня была эта проблема на загруженных терминальных серверах с большими экранами.

Если у вас много дескрипторов файлов, вы можете проверить Process Explorer и взглянуть на открытые дескрипторы файлов вашего процесса и увидеть любые необычные. Или используйте WinDbg с ! htrace.

person Lars Truijens    schedule 02.02.2010
comment
Лучший ответ до сих пор. Показывает ресурсы GDI по типу. Сначала посмотрим, как это работает у клиента, прежде чем принимать и т. Д. - person Marco van de Voort; 03.02.2010
comment
Можете указать имя these free tools? Потому что сейчас эта ссылка недействительна. - person Zam; 13.09.2016

Существует небольшая вероятность того, что ошибка вводит в заблуждение. VCL наивно сообщает EOutOfResources, если не может получить DC для окна (см. TWinControl.GetDeviceContext в Controls.pas).

Я говорю «наивно», потому что есть и другие причины, по которым GetDC () может возвращать нулевой дескриптор, а VCL должен сообщать об ошибке ОС, а не предполагать состояние нехватки ресурсов ( для того, чтобы это было надежно возможно, требуется проверка версии Windows, но VCL может и должна это взять).

У меня была ситуация, когда я получал ошибку EOutOfResources в результате того, что дескриптор окна становился недействительным. Как только я обнаружил настоящую проблему, найти причину и устранить ее стало просто, но я потратил много, много часов, пытаясь найти несуществующую утечку ресурсов.

Если возможно, я бы исследовал трассировку стека, ведущую к этому исключению - если оно исходит от TWinControl.GetDeviceContext, то проблема может быть не в том, что вы думаете (конечно, невозможно сказать, что это может быть, но устранение невозможного - всегда первый шаг к поиску решения, каким бы невероятным оно ни было).

person Deltics    schedule 02.02.2010
comment
Отличный совет. В чем может быть причина, по которой GetDC возвращает NULL? - person Z80; 30.10.2011
comment
@Altar - самый простой и тот, который поймал меня в моем случае, - это когда HWND, переданный GetDC (), недействителен. В моем случае это было окно, которое уже было разрушено. Как только я обнаружил это [с помощью моего собственного вызова GetLastError ()], на самом деле было довольно просто увидеть, как и почему HWND [неожиданно] уничтожается, прежде чем я получил DC, и исправить эту проблему. - person Deltics; 31.10.2011

Я сталкивался с этой проблемой раньше. Из того, что я смог сказать, Delphi может выдать EOutOfResources в любое время, когда Windows API возвращает ERROR_NOT_ENOUGH_MEMORY, и (как обсуждают другие ответы здесь) Windows может возвращать ERROR_NOT_ENOUGH_MEMORY для различных условий.

В моем случае EOutOfResources был вызван TBitmap - в частности, вызов TBitmap для CreateCompatibleBitmap, который он использует с PixelFormat по умолчанию для pfDevice. Очевидно, Windows может применять довольно строгие общесистемные ограничения на объем памяти, доступной для зависящих от устройства растровых изображений (см., Например, это обсуждение), даже если в остальном ваша система имеет много памяти и много ресурсов GDI. (Эти общесистемные ограничения, по-видимому, вызваны тем, что Windows может выделять зависящие от устройства растровые изображения в памяти видеокарты.)

Решение состоит в том, чтобы вместо этого просто использовать независимые от устройства растровые изображения (DIB) (хотя они могут не обеспечивать такую ​​высокую производительность). Для этого в Delphi установите для TBitmap.PixelFormat любое значение, кроме pfDevice. В этой статье базы знаний описывается, как выбрать оптимальный формат DIB для устройства, хотя Обычно я просто использую pf32Bit вместо того, чтобы пытаться определить оптимальный формат для каждого из мониторов, на которых отображается приложение.

person Josh Kelley    schedule 08.08.2013
comment
Я проверил, и источник устанавливает для всех изображений значение pf8bit после создания. Перезапись убила tbitmap в пользу собственного типа изображения. (на основе массива байтов. Я предполагаю что-то похожее, но только для tmetafile. - person Marco van de Voort; 10.08.2013
comment
Вы помните имя этого обсуждения в списке рассылки? ссылка сейчас мертва. - person Wolf; 27.01.2015
comment
@Wolf - я не помню, но я нашел другое обсуждение (или другую копию того же обсуждения) и обновил ссылку. Спасибо. - person Josh Kelley; 27.01.2015

В большинстве случаев я видел EOutOfResources, это была какая-то утечка дескриптора.

Вы пробовали что-то вроде MadExcept?

- Джерун

person Jeroen Wiert Pluimers    schedule 01.02.2010
comment
Я знаю, поэтому ищу способ найти сводку по типу утечки ресурсов. Раньше я делал это с помощью memproof (для этого afaik перехватил много вызовов win32), но это слишком сильно тормозит. - person Marco van de Voort; 02.02.2010
comment
Я предлагаю вам сосредоточиться на дампах трассировки стека и понять это из контекста, а не из резюме ресурсов. Однако я считаю, что AQTime может связать отдельные подсчеты ресурсов со строками кода, которые их создали, что может быть вам полезно. - person Warren P; 23.07.2010
comment
MadExcept 4 может фиксировать утечки ресурсов (только для 32-битных приложений). - person Nicholas Ring; 03.11.2012

«Я попытался добавить отладочный код, чтобы сузить проблему. Я обнаружил, что исключением является EOutofResources при сохранении файла (сохранение файла может происходить тысячи раз в день)».

Я снимаю здесь в темноте, но может быть, вы используете Windows API для (GetTempFileName) создания временного файла, и вы выдуваете некоторые индексы файловой системы или забываете закрыть дескриптор файла?

В любом случае, я согласен с вашим предположением о том, что это проблема с обработкой файлов. Это кажется наиболее вероятным с учетом ваших симптомов и диагноза.

person Allen Bauer    schedule 02.02.2010
comment
Нет gettempfilename. Я сразу подумал о файловых дескрипторах и проверил. Как бы то ни было, все основные сохранения выполняются в очень простых и доступных для обзора методах thread .execute (все обычно выполняемые сохранения сосредоточены в потоке хранилища, поскольку они блокируются слишком долго). Кажется, что ни один из потоков не останавливается (что делает необработанное исключение), но добавленное в него ведение журнала начинает возвращать этот (обработанный) EOutofResources. - person Marco van de Voort; 02.02.2010

Также попробуйте проверить количество дескрипторов для приложения с помощью Process Explorer от SysInternals. Утечки в ручках могут быть очень опасными, и они медленно нарастают с течением времени.

person Runner    schedule 02.02.2010

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

Счетчик дескрипторов, количество GDI и пользовательских объектов не увеличивается, и при этом ничего не создается. Ответ Deltic показывает угловые случаи, когда сообщение является отвлекающим маневром, и Аллен предполагает, что даже запись файла может вызвать эту ошибку.

На данный момент лучшая стратегия, которую я нашел для их поиска, - это использовать либо трассировку стека JCL JCLDEBUG, либо функции сохранения отчета об исключениях в MadExcept для генерации контекстной информации, чтобы выяснить, что на самом деле дает сбой.

Во-вторых, AQTime содержит множество инструментов, которые могут вам помочь, в том числе профилировщик ресурсов, который может поддерживать связи между тем, где находится код, создавший ресурсы, и тем, как он был вызван, а также подсчет общего количества дескрипторов. Он может захватывать результаты MID RUN и поэтому не ограничивается обнаружением неиспользованных ресурсов после выхода. Итак, запустите AQTime, сделайте захват результатов в середине выполнения, подождите несколько часов и снова захватите, и у вас должно быть два момента времени для сравнения счетчиков дескрипторов. На всякий случай это очевидная вещь. Но, как мудро указывает Deltics, этот класс исключения возникает в тех случаях, когда этого, вероятно, не должно было быть.

person Warren P    schedule 23.07.2010
comment
Оглядываясь назад, я подозреваю, что это было связано с TMetafile. - person Marco van de Voort; 29.04.2011

Я потратил весь сегодня на поиски этого вопроса. Я нашел множество полезных ресурсов, указывающих мне в сторону GDI, с тем фактом, что я использую GDI + для создания высокоскоростной анимации непосредственно в основной форме с помощью timer / invalidate / onpaint (анимация выполняется в отдельном потоке). У меня также есть панель в этой форме с некоторыми динамически создаваемыми элементами управления, чтобы пользователь мог вносить изменения в анимацию.

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

Я внимательно следил за своим кодом и удостоверился, что у меня нет дескрипторов, связанных с GDI. На самом деле, согласно диспетчеру задач, все мое приложение имеет менее 300 дескрипторов. Тем не менее, эта ошибка будет появляться случайным образом. И это всегда будет соответствовать простейшему действию, связанному с пользовательским интерфейсом, например, просто наведению указателя мыши на стандартный элемент управления VCL.

Решение

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

То есть, по крайней мере, с 1 часом интенсивного тестирования.

Анимация работает без ошибок

[Скрещенные пальцы]

person Jerry Dodge    schedule 23.09.2019
comment
Я рисовал на панели и рисовал стрейч-рисунком. - person Marco van de Voort; 23.09.2019