使用Visual Studio确定堆栈空间

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

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

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

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

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

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 (ie accessed) portion of the stack return mbi.RegionSize; } 

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

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

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

 #include  #include  #include  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()); // ... } 

堆栈也不像您期望的那样工作。 堆栈是一个线性的页面序列,最后一个页面(顶部)标有页面保护位。 触摸此页面时,将删除保护位,并可以使用该页面。 为了进一步增长,我们会分配一个新的防护页面。

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

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

您可以使用GetThreadContext()函数来确定线程的当前堆栈指针。 然后使用VirtualQuery()查找此指针的堆栈基础。 减去这两个指针将给出给定线程的堆栈大小。