C ++ / Windows: Как сообщить об исключении нехватки памяти (bad_alloc)?

В настоящее время я работаю над системой отчетов об ошибках на основе исключений для приложений Windows MSVC ++ (9.0) (т.е. структуры и типы исключений / наследование, стек вызовов, отчеты об ошибках, ведение журнала и т. д.).

Теперь у меня вопрос: как правильно сообщать и регистрировать ошибку нехватки памяти?

Когда возникает эта ошибка, например, как выдается bad_alloc операцией new может быть много недоступных «функций», в основном касающихся дальнейшего выделения памяти. Обычно я передаю исключение приложению, если оно было добавлено в библиотеку, а затем используя сообщение ящики и файлы журнала ошибок, чтобы сообщать и регистрировать это. Другой способ (в основном для служб) - использовать журнал событий Windows.
Основная проблема, с которой я столкнулся. e - собрать сообщение об ошибке. Чтобы предоставить некоторую информацию об ошибке, я хотел бы определить статическое сообщение об ошибке (может быть строковым литералом, лучше записью в файле сообщения, затем с помощью FormatMessage) и включить некоторую информацию времени выполнения. например стек вызовов.
Функции / методы, необходимые для этого, используют либо

  • STL ( std :: string, std :: stringstream, std :: ofstream )
  • CRT ( swprintf_s, fwrite )
  • или Win32 API ( StackWalk64, MessageBox, FormatMessage, ReportEvent, WriteFile )

Помимо документации в MSDN, все они больше (Win32) или меньше (STL) с закрытым исходным кодом в Windows, поэтому я действительно не знаю, как они себя ведут при проблемах с нехваткой памяти.

Чтобы доказать, что могут быть проблемы, я написал тривиальное маленькое приложение, вызывающее bad_alloc:

int main()
{
    InitErrorReporter();  

    try
    {
        for(int i = 0; i < 0xFFFFFFFF; i++)
        {
            for(int j = 0; j < 0xFFFFFFFF; j++)
            {
                char* p = new char;
            }
        }
    }catch(bad_alloc& e_b)
    {
        ReportError(e_b);
    }

    DeinitErrorReporter();

    return 0;
}

Провел два экземпляра без подключенного отладчика (в конфигурации выпуска, VS 2008), но «ничего не произошло», т.е. без ошибок коды из ReportEvent или WriteFile, которые я использовал для внутренних отчетов об ошибках. Затем запустили один экземпляр с отладчиком и один без отладчика, и пусть они попытаются сообщить о своих ошибках одну за другой, используя точку останова в строке ReportError. Это отлично работало для экземпляра с подключенным отладчиком (правильно сообщал и регистрировал ошибку, даже используя LocalAlloc без проблем)! Но таскмен показал странное поведение, когда много памяти освобождалось до выхода из приложения, я полагаю, когда возникает исключение.


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

Заранее благодарю!

8
задан dyp 23 August 2010 в 19:58
поделиться

3 ответа

  • предварительно выделите буфер(ы), которые вам нужны
  • свяжите статически и используйте _beginthreadex вместо CreateThread (иначе функции CRT могут не работать) -- ИЛИ -- реализуйте строку concat / i2a самостоятельно
  • Используйте MessageBox ( MB_SYSTEMMODAL | MB_OK) MSDN упоминает об этом для сообщения об условиях OOM (и некоторые блогеры MS описали это поведение как задуманное: окно сообщения не будет выделять память.)

Ведение журнала сложнее, по крайней мере, файл журнала должен быть открыт. уже.

Вероятно, лучше всего использовать FILE_FLAG_NO_BUFFERING и FILE_FLAG_WRITE_THROUGH, чтобы избежать попыток буферизации. Первый требует, чтобы запись и ваши буферы памяти были выровнены по секторам (т. е. вам нужно запросить GetDiskFreeSpace, выровнять ваш буфер по этому параметру и записывать только в файлы, кратные размеру сектора, и в блоки, кратные размеру сектора. Я не уверен, что это необходимо или помогает, но общесистемный OOM, при котором каждое распределение терпит неудачу, трудно смоделировать

.
2
ответ дан 5 December 2019 в 22:15
поделиться

Вы не можете использовать функции CRT или MessageBox для обработки OOM, так как им может потребоваться память, как вы описываете. Единственная действительно безопасная вещь, которую вы можете сделать, это выделить кусок памяти при запуске, в который вы можете записать информацию и открыть дескриптор файла или канала, а затем WriteFile в него, когда вы OOM выйдете.

0
ответ дан 5 December 2019 в 22:15
поделиться

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

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

Именно так Windows может помещать память, используемую процессами, в файл подкачки на жестком диске, при этом эти процессы не знают о том, что произошло.

Таким же образом один процесс может выделять памяти больше, чем имеет физическая оперативная память машины, и при этом работать. Например, программа, работающая на машине с 512 МБ ОЗУ, может по-прежнему выделять 1 ГБ памяти. Windows просто не могла бы хранить все это в ОЗУ одновременно, и часть этого была бы в файле подкачки. Но программа не знала.

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

Каждый процесс должен заботиться только о себе. Так что идея освобождения предварительно выделенного куска памяти на самом деле очень жизнеспособна.

1
ответ дан 5 December 2019 в 22:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: