使用Visual Studio确定堆栈空间

JXG*_*JXG 7 c memory stack memory-management visual-studio

我在Visual Studio 2005中用C编程.我有一个多线程程序,但这在这里并不是特别重要.

如何确定(大约)我的线程使用多少堆栈空间?

我计划使用的技术是将堆栈内存设置为某个预定值,比如0xDEADBEEF,运行程序很长时间,暂停程序,并调查堆栈.

如何使用Visual Studio读取和写入堆栈内存?

编辑:例如,参见"如何确定最大堆栈使用量". 那个问题谈到了一个嵌入式系统,但在这里我试图在常规PC上确定答案.

atz*_*tzz 15

Windows不会立即提交堆栈内存; 相反,它为它保留地址空间,并在访问时逐页提交.阅读此页面了解更多信息.

因此,堆栈地址空间由三个连续区域组成:

  • 保留但未提交的内存,可用于堆栈增长(但从未访问过);
  • Guard页面,从未被访问过,用于在访问时触发堆栈增长;
  • 提交的内存,即线程访问过的堆栈内存.

这允许我们构造一个获取堆栈大小的函数(具有页面大小粒度):

static size_t GetStackUsage()
{
    MEMORY_BASIC_INFORMATION mbi;
    VirtualQuery(&mbi, &mbi, sizeof(mbi));
    // now mbi.AllocationBase = reserved stack memory base address

    VirtualQuery(mbi.AllocationBase, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe reserved (uncommitted) portion of the stack
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the guard page
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the committed (i.e. accessed) portion of the stack

    return mbi.RegionSize;
}
Run Code Online (Sandbox Code Playgroud)

需要考虑的一件事:CreateThread允许指定初始堆栈提交大小(通过dwStackSize参数,STACK_SIZE_PARAM_IS_A_RESERVATION未设置标志时).如果此参数非零,则仅当堆栈使用量大于dwStackSize值时,我们的函数才会返回正确的值.

  • @Philip - 堆栈确实增长了(至少在x86上).我正在添加因为`VirtualQuery`返回内存分配区域的基地址 - 向下增长堆栈的最后一个(理论上)可用字节的地址.在具有向上增长的堆栈的平台上,第一个`VirtualQuery`调用将给出所需的结果.我想我可以用图片来说明它; 当我有更多时间时,我甚至可能会做. (2认同)

Ser*_*kov 7

您可以使用Win32线程信息块中的信息

当你想在一个线程中找出它使用多少堆栈空间时,你可以这样做:

#include <windows.h>
#include <winnt.h>
#include <intrin.h>

inline NT_TIB* getTib()
{
    return (NT_TIB*)__readfsdword( 0x18 );
}
inline size_t get_allocated_stack_size()
{
    return (size_t)getTib()->StackBase - (size_t)getTib()->StackLimit;
}

void somewhere_in_your_thread()
{
    // ...
    size_t sp_value = 0;
    _asm { mov [sp_value], esp }
    size_t used_stack_size = (size_t)getTib()->StackBase - sp_value;

    printf("Number of bytes on stack used by this thread: %u\n", 
           used_stack_size);
    printf("Number of allocated bytes on stack for this thread : %u\n",
           get_allocated_stack_size());    
    // ...
}
Run Code Online (Sandbox Code Playgroud)


MSa*_*ers 1

堆栈也没有按照您期望的方式工作。堆栈是页面的线性序列,其中最后一个(顶部)用页面保护位标记。当该页被触摸时,保护位被移除,并且该页可以被使用。为了进一步增长,分配一个新的保护页。

因此,您想要的答案是 gaud 页的分配位置。但是您提出的技术会触及有问题的页面,因此它会使您尝试测量的东西无效。

确定(堆栈)页是否具有保护位的非侵入性方法是 via VirtualQuery()

  • 实际上,这是正确的答案,因为分配给堆栈的页面专门分配给该堆栈和线程。因此,堆栈大小始终为页数。另请参见 MSVC 编译器选项 - “初始堆栈空间”等选项以页面大小的倍数指定。 (3认同)