Охота вниз EOutOfResources

Вопрос:

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

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

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

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

Фон:

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

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

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

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

Обновления:

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

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

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

------------------- UPDATE

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

- Йерун

Ответ 4

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

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

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

Ответ 5

Я столкнулся с этой проблемой до. Из того, что я мог сказать, 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. В этой статье KB описывается, как выбрать оптимальный формат DIB для устройства, хотя я обычно просто использую pf32Bit вместо того, чтобы пытаться определить оптимальный формат для на каждом из мониторов отображается приложение.

Ответ 6

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

Ответ 7

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

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

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

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

Ответ 8

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

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

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

Решение

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

Это примерно 1 час энергичного тестирования.

Animation Working Without Errors

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