C ++ / Windows: как сообщить об исключении нехватки памяти (bad_alloc)? - PullRequest
8 голосов
/ 20 августа 2010

В настоящее время я работаю над системой отчетов об ошибках на основе исключений для приложений Windows MSVC ++ (9.0) (т. Е. Структуры и типы исключений / наследование, стек вызовов, отчеты об ошибках и ведение журнала и т. Д.).
Теперь у меня вопрос: как правильно сообщить и зарегистрировать ошибку нехватки памяти?
Когда возникает эта ошибка, например, как bad_alloc, генерируемая операцией new, может быть недоступно много «функций», в основном касающихся дальнейшего выделения памяти.Обычно я передаю исключение приложению, если оно было брошено в lib, а затем использую окна сообщений и файлы журнала ошибок, чтобы сообщать и регистрировать его.Другой способ (в основном для служб) - использовать журнал событий Windows.
Основная проблема, с которой я сталкиваюсь, - собрать сообщение об ошибке. Чтобы предоставить некоторую информацию об ошибке, я бы хотел определить статическийсообщение об ошибке (может быть строковым литералом, лучше запись в файле сообщения, затем с использованием 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;
}

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

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

Заранее спасибо!

Ответы [ 4 ]

3 голосов
/ 20 августа 2010

«Освобождение заранее выделенного пространства кучи ...». Именно об этом я и думал, читая твой вопрос. Но я думаю, что вы можете попробовать это. Каждый процесс имеет свое собственное пространство виртуальной памяти. С другими процессами, занимающими много памяти, это все еще может работать, если весь компьютер работает.

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

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

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

1 голос
/ 20 августа 2010

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

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

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

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

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

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

0 голосов
/ 21 августа 2010

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

...