C++/Windows:如何报告内存不足异常(bad_alloc)?

发布于 2024-09-15 19:51:40 字数 1560 浏览 3 评论 0原文

我目前正在为 Windows MSVC++ (9.0) 应用程序开发基于异常的错误报告系统(即异常结构和类型/继承、调用堆栈、错误报告和日志记录等)。

我现在的问题是:如何正确举报和举报?记录内存不足错误?

当此错误发生时,例如,作为 new 操作抛出的 bad_alloc,可能有许多“功能”不可用,主要涉及进一步的内存分配。通常,如果异常被抛出到库中,我会将异常传递给应用程序,然后使用消息框和错误日志文件来报告和记录它。另一种方法(主要用于服务)是使用 Windows 事件日志。
我遇到的主要问题是组装错误消息。 为了提供一些错误信息,我想定义一条静态错误消息(可能是字符串文字,最好是消息文件中的条目,然后使用 FormatMessage)并包含一些运行时信息,例如调用堆栈。
为此所需的函数/方法使用

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

除了在 MSDN 上有记录之外,它们在 Windows 中或多或少(Win32)或更少(STL)都是封闭源代码,所以我真的不知道它们是如何实现的在低内存问题下表现。

为了证明可能存在问题,我编写了一个引发 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] 消耗大量内存,因此释放预分配的堆空间并不是一种安全的解决方案,以避免想要报告的进程出现低内存环境错误。

提前谢谢您!

I'm currently working on an exception-based error reporting system for Windows MSVC++ (9.0) apps (i.e. exception structures & types / inheritance, call stack, error reporting & logging and so on).

My question now is: how to correctly report & log an out-of-memory error?

When this error occurs, e.g. as an bad_alloc thrown by the new op, there may be many "features" unavailable, mostly concerning further memory allocation. Normally, I'd pass the exception to the application if it has been thrown in a lib, and then using message boxes and error log files to report and log it. Another way (mostly for services) is to use the Windows Event Log.
The main problem I have is to assemble an error message.
To provide some error information, I'd like to define a static error message (may be a string literal, better an entry in a message file, then using FormatMessage) and include some run-time info such as a call stack.
The functions / methods necessary for this use either

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

Besides being documented on the MSDN, all of them more (Win32) or less (STL) closed source in Windows, so I don't really know how they behave under low memory problems.

Just to prove there might be problems, I wrote a trivial small app provoking a 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;
}

Ran two instances w/o debugger attached (in Release config, VS 2008), but "nothing happened", i.e. no error codes from the ReportEvent or WriteFile I used internally in the error reporting. Then, launched one instance with and one w/o debugger and let them try to report their errors one after the other by using a breakpoint on the ReportError line. That worked fine for the instance with the debugger attached (correctly reported & logged the error, even using LocalAlloc w/o problems)! But taskman showed a strange behaviour, where there's a lot of memory freed before the app exits, I suppose when the exception is thrown.

Please consider there may be more than one process [edit] and more than one thread [/edit] consuming much memory, so freeing pre-allocated heap space is not a safe solution to avoid a low memory environment for the process which wants to report the error.

Thank you in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

随风而去 2024-09-22 19:51:40

“释放预分配的堆空间......”。这正是我读你的问题时的想法。但我想你可以尝试一下。每个进程都有自己的虚拟内存空间。当其他进程消耗大量内存时,如果整个计算机都在工作,这仍然可以工作。

"Freeing pre-allocated heap space...". This was exactly that I thought reading your question. But I think you can try it. Every process has its own virtual memory space. With another processes consuming a lot of memory, this still may work if the whole computer is working.

第七度阳光i 2024-09-22 19:51:40
  • 预分配
  • 静态链接所需的缓冲区并使用 _beginthreadex 而不是 CreateThread(否则,CRT 函数可能会失败) -- 或者 -- 自己实现字符串 concat / i2a
  • 使用 MessageBox (MB_SYSTEMMODAL | MB_OK) MSDN 在报告中提到了这一点OOM 情况(一些 MS 博主按照预期描述了这种行为:消息框不会分配内存。)

日志记录更困难,至少需要打开日志文件。

可能最好使用FILE_FLAG_NO_BUFFERINGFILE_FLAG_WRITE_THROUGH,以避免任何缓冲尝试。第一个要求写入和内存缓冲区是扇区对齐的(即,您需要查询 GetDiskFreeSpace,以此对齐缓冲区,并且仅写入“扇区大小的倍数”文件偏移量,并且写入扇区大小的倍数的块中。我不确定这是否有必要或有帮助,但是每个分配失败的系统范围的 OOM 很难模拟。

  • pre-allocate the buffer(s) you need
  • link statically and use _beginthreadex instead of CreateThread (otherwise, CRT functions may fail) -- OR -- implement the string concat / i2a yourself
  • Use MessageBox (MB_SYSTEMMODAL | MB_OK) MSDN mentions this for reporting OOM conditions (and some MS blogger described this behavior as intended: the message box will not allocate memory.)

Logging is harder, at the very least, the log file needs to be open already.

Probably best with FILE_FLAG_NO_BUFFERING and FILE_FLAG_WRITE_THROUGH, to avoid any buffering attempts. The first one requires that writes and your memory buffers are sector aligned (i.e. you need to query GetDiskFreeSpace, align your buffer by that, and write only to "multiple of sector size" file offsets, and in blocks that are multiples of sector size. I am not sure if this is necessary, or helps, but a system-wide OOM where every allocation fails is hard to simulate.

山色无中 2024-09-22 19:51:40

请考虑可能有多个进程消耗大量内存,因此释放预分配的堆空间并不是一种安全的解决方案,以避免想要报告错误的进程出现低内存环境。

在 Windows(和其他现代操作系统)下,每个进程都有自己的地址空间(也称为内存),与其他正在运行的进程分开。所有这些都与机器中的实际 RAM 分开。操作系统已将进程地址空间虚拟化,远离物理 RAM。

这就是 Windows 能够将进程使用的内存推送到硬盘上的页面文件中,而这些进程对发生的情况一无所知。

这也是单个进程可以分配比机器物理 RAM 更多内存但仍然运行的方式。例如,在具有 512MB RAM 的计算机上运行的程序仍然可以分配 1GB 内存。 Windows 无法同时将所有内容保存在 RAM 中,其中一些内容将保存在页面文件中。但程序不知道。

因此,如果一个进程分配内存,它不会导致另一个进程有更少的内存可供使用。每个过程都是独立的。

每个进程只需要关心它自己。因此释放预先分配的内存块的想法实际上是非常可行的。

Please consider there may be more than one process consuming much memory, so freeing pre-allocated heap space is not a safe solution to avoid a low memory environment for the process which wants to report the error.

Under Windows (and other modern operating systems), each process has its own address space (aka memory) separate from every other running process. And all of that is separate from the literal RAM in the machine. The operating system has virtualized the process address space away from the physical RAM.

This is how Windows is able to push memory used by processes into the page file on the hard disk without those processes having any knowledge of what happened.

This is also how a single process can allocate more memory than the machine has physical RAM and yet still run. For instance, a program running on a machine with 512MB of RAM could still allocate 1GB of memory. Windows would just couldn't keep all of it in the RAM at the same time and some of it would be in the page file. But the program wouldn't know.

So consequently, if one process allocates memory, it does not cause another process to have less memory to work with. Each process is separate.

Each process only needs to worry about itself. And so the idea of freeing a pre-allocated chunk of memory is actually very viable.

花之痕靓丽 2024-09-22 19:51:40

您不能使用 CRT 或 MessageBox 函数来处理 OOM,因为它们可能需要内存,正如您所描述的。您可以做的唯一真正安全的事情是在启动时分配一块内存,您可以将信息写入其中并打开文件或管道的句柄,然后在 OOM 时向其中写入文件。

You can't use CRT or MessageBox functions to handle OOM since they might need memory, as you describe. The only truly safe thing you can do is alloc a chunk of memory at startup you can write information into and open a handle to a file or a pipe, then WriteFile to it when you OOM out.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文