【问题标题】:How to detect the amount of stack space available to my program?如何检测我的程序可用的堆栈空间量?
【发布时间】:2010-11-23 15:32:26
【问题描述】:

我的 Win32 C++ 应用程序充当 RPC 服务器 - 它具有一组用于处理请求的函数,并且 RPC 运行时创建一个单独的线程并在该线程中调用我的一个函数。

在我的函数中,我有一个 std::auto_ptr 用于控制在编译时已知大小的堆分配 char[] 数组。它accidentially works when compiled with VC++ 但根据 C++ 标准它是未定义的行为,我想摆脱它。

我有两个选择:std::vector 或堆栈分配的数组。因为我不知道为什么会有一个堆分配的数组,所以我想考虑用一个堆栈分配的数组来替换它。该数组有 10k 个元素,如果 RPC 运行时产生一个堆栈非常小的线程,我可能会面临堆栈溢出。

我想检测通常为线程分配了多少堆栈空间以及我的函数可以使用多少堆栈空间(它的被调用者肯定会消耗一些分配的空间)。我怎么能这样做?

【问题讨论】:

  • 你有第三种选择,因为数组大小是已知的:struct wrapper { char buffer[N]; }; std::auto_ptr<wrapper> ptr;
  • 不错的解决方法。我想我会使用它而不是冒险崩溃。

标签: c++ windows winapi visual-c++


【解决方案1】:

在 Windows 中,默认堆栈大小为 1MB,因此您不太可能仅使用 10k 数组就发生堆栈溢出。也就是说,我认为在堆栈上分配这么多内存是一种不好的做法,如果可以的话,你应该尽量倾向于动态分配它。还有Scoped Array,它为自动管理数组而定义良好 - 与vector 类不同,它是不可复制的。

【讨论】:

  • 我真的不知道在堆栈上分配巨大的数组有什么不好,除了堆栈溢出的风险。
  • 堆栈溢出的风险是不好的。
【解决方案2】:

如果您无权访问CreateThread 调用,或者如果它是主线程,请查看 EXE 的默认线程大小,我不知道有任何方法可以直接使用 API 计算堆栈大小PE 标头。

在您的情况下,为了安全起见,我会在堆上进行分配,即使 10K 的小数据数组不太可能在非递归场景中最大化堆栈。

但是,如果小心操作,您可以探测堆栈限制。当您触摸堆栈时(通过保护页面),堆栈将在 4K 页面中提交,直到达到限制,然后 Windows 将抛出堆栈溢出异常。当异常被分派时,还剩下一页堆栈,因此异常分派逻辑本身(包括过滤器函数)可以执行 - 但 Windows 抛出异常因为它无法分配另一个保护页 .这意味着下一次堆栈溢出或探测不会导致堆栈溢出异常,而是访问冲突。因此,为了使探测可靠地工作(特别是可重复地),您需要解除探测分配的内存并恢复保护页。

This article on KB describes how to decommit stack memory and reinstate the guard page. 它使用递归和 10,000 字节增量进行探测;编译器默认实现自己的堆栈探测,用于本地>4KB 的堆栈分配,以便堆栈增长机制正常工作。

【讨论】:

    【解决方案3】:

    "std::auto_ptr 用于 控制堆分配的 char[] ... 这是未定义的行为,根据 C++"

    这是错误的假设! STL 的 auto_ptr 对行为有精确的描述。如果您担心在复杂的分配审查期间失去控制,可以使用引用计数器模式来控制破坏堆分配的数组。

    【讨论】:

    • auto_ptr::~auto_ptr() 调用 delete,而不是 delete[] 并且将 new[] 与 delete 配对是未定义的行为。那是我的问题。
    • 这是不正确的。您不应该使用 auto_ptr 来控制数组 - auto_ptr 的行为被定义为使用 operator delete,这是堆分配数组的未定义行为。
    • @sharptooth - 这就是为什么我在谈论分配内存的自己的控制器,即使有引用计数。当然,更好的方法是利用 std::vector、deque ......但无论如何,堆栈是有史以来最糟糕的解决方案。
    • "std::auto_ptr 用于控制堆分配的char[]" 这几乎是您可以获得的未定义行为。
    • @sbi 堆分配数组前面的点超过分配的堆栈。在堆栈中分配数千字节是最糟糕的解决方案。
    【解决方案4】:

    我第二个 1800 信息:

    • 如果可以,请在堆上分配数据。它更安全(例如,缓冲区溢出更难利用)并且在您以后需要扩展设计时(不是如果)更灵活。

    • 使用 std::vector、boost::scoped_array 或 boost::shared_array。

    我知道它没有回答您关于检测堆栈大小的问题,但我认为这是对您问题的合乎逻辑的回答。

    【讨论】:

      【解决方案5】:

      我不确定你在追求什么。

      如果您只想要典型的数字,那么请继续尝试!创建一个具有嵌套作用域的函数,每个作用域分配更多的堆栈空间。每个范围内的输出。看看事情发展到什么程度。

      如果您想在具体情况下获得具体数字,请问自己拥有这些数字后您想做什么?分支到不同的实现?这听起来像是一个维护问题,使用它应该是非常合理的。你期望获得什么?这真的值得这么麻烦吗?

      我同意 10k 通常不成问题。因此,如果您的代码不是关键任务,请继续使用boost::array(或std::tr1::array,如果您的标准库附带它)。否则,只需使用std::vector,或者,如果您认为必须使用boost::scoped_array(或std::tr1::scoped_array,如果您的标准库附带它)。

      【讨论】:

        猜你喜欢
        • 2021-12-31
        • 2013-01-15
        • 2018-08-03
        • 2016-09-19
        • 2012-01-09
        • 1970-01-01
        • 1970-01-01
        • 2010-10-19
        • 2010-09-17
        相关资源
        最近更新 更多