c# - 在 C# 中检查堆栈大小

标签 c# multithreading stack

有没有办法在 C# 中检查线程堆栈大小?

最佳答案

这是 if you have to ask, you can't afford it 的案例(Raymond Chen 先说。)如果代码依赖于足够的堆栈空间以致必须首先检查,那么重构它以使用显式 Stack<T> 可能是值得的。对象代替。 John 关于改用分析器的评论是有道理的。

也就是说,事实证明有一种方法可以估计剩余的堆栈空间。它并不精确,但对于评估您离谷底有多近的目的来说已经足够有用了。以下内容主要基于 excellent article by Joe Duffy .

我们知道(或将做出假设):

  1. 堆栈内存分配在一个连续的 block 中。
  2. 堆栈“向下”增长,从高地址到低地址。
  3. 系统在分配的堆栈空间底部附近需要一些空间,以便妥善处理堆栈外异常。我们不知道确切的预留空间,但我们会尝试保守地限制它。

根据这些假设,我们可以调用 VirtualQuery获得分配堆栈的起始地址,并从一些堆栈分配变量的地址中减去它(使用不安全代码获得)。进一步减去我们对系统在堆栈底部所需空间的估计将得到一个估计的可用空间。

下面的代码通过调用递归函数并写出剩余的估计堆栈空间(以字节为单位)来演示这一点:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1 {
    class Program {
        private struct MEMORY_BASIC_INFORMATION {
            public uint BaseAddress;
            public uint AllocationBase;
            public uint AllocationProtect;
            public uint RegionSize;
            public uint State;
            public uint Protect;
            public uint Type;
        }

        private const uint STACK_RESERVED_SPACE = 4096 * 16;

        [DllImport("kernel32.dll")]
        private static extern int VirtualQuery(
            IntPtr                          lpAddress,
            ref MEMORY_BASIC_INFORMATION    lpBuffer,
            int                             dwLength);


        private unsafe static uint EstimatedRemainingStackBytes() {
            MEMORY_BASIC_INFORMATION    stackInfo   = new MEMORY_BASIC_INFORMATION();
            IntPtr                      currentAddr = new IntPtr((uint) &stackInfo - 4096);

            VirtualQuery(currentAddr, ref stackInfo, sizeof(MEMORY_BASIC_INFORMATION));
            return (uint) currentAddr.ToInt64() - stackInfo.AllocationBase - STACK_RESERVED_SPACE;
        }

        static void SampleRecursiveMethod(int remainingIterations) {
            if (remainingIterations <= 0) { return; }

            Console.WriteLine(EstimatedRemainingStackBytes());

            SampleRecursiveMethod(remainingIterations - 1);
        }

        static void Main(string[] args) {
            SampleRecursiveMethod(100);
            Console.ReadLine();
        }
    }
}

这是前 10 行输出(intel x64、.NET 4.0、调试)。给定 1MB 的默认堆栈大小,计数似乎是合理的。

969332
969256
969180
969104
969028
968952
968876
968800
968724
968648

为简洁起见,上面的代码假定页面大小为 4K。虽然这适用于 x86 和 x64,但它可能不适用于其他受支持的 CLR 体系结构。你可以调用 GetSystemInfo获取机器的页面大小(SYSTEM_INFO 结构的 dwPageSize)。

请注意,此技术不是特别便携,也不是面向 future 的。 pinvoke 的使用限制了这种方法对 Windows 主机的实用性。关于 CLR 堆栈的连续性和增长方向的假设可能适用于当前的 Microsoft 实现。然而,我(可能是有限的)对 CLI standard 的阅读(公共(public)语言基础设施,PDF,长篇阅读)似乎不需要那么多的线程堆栈。就 CLI 而言,每个方法调用都需要一个堆栈帧;然而,它并不关心堆栈是否向上增长,局部变量堆栈是否与返回值堆栈分开,或者堆栈帧是否分配在堆上。

关于c# - 在 C# 中检查堆栈大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2901185/

相关文章:

C# 单管道语法

c++ - Qt 界面卡住后台任务

Java 堆栈 push() 与 add()

python - 将此函数从递归转换为迭代

Java堆栈应用程序

c# - 检查嵌套 json 中的键名

c# - PublicClientApplicationBuilder 和 TokenCacheHelper

c# - 由于 1 个编译器警告,MSBuild 4 无法构建 VS2008 csproj

c++ - 多线程同步STD cout输出

c# - 在 ContinueWith block 内使用 await 的意外行为