Проверка размера стека в C#

Существует ли способ проверить, что потоки складывают размер в C#?

15
задан Gjorgji 24 May 2010 в 23:58
поделиться

1 ответ

Это случай , если вам нужно спросить, вы не можете себе этого позволить (Рэймонд Чен сказал это первым.) Если код зависит от наличия достаточного пространства стека в той степени, в которой он имеет для первой проверки, возможно, стоит провести рефакторинг, чтобы вместо этого использовать явный объект Stack . В комментарии Джона об использовании профилировщика есть заслуга.

Тем не менее, оказывается, что есть способ оценить оставшееся пространство стека. Это не совсем точно, но достаточно полезно для оценки того, насколько вы близки ко дну. Нижеследующее в значительной степени основано на отличной статье Джо Даффи .

Мы знаем (или будем делать предположения), что:

  1. Стековая память распределяется в непрерывном блоке.
  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, отладка). Учитывая размер стека по умолчанию в 1 МБ, подсчеты кажутся правдоподобными.

969332
969256
969180
969104
969028
968952
968876
968800
968724
968648

Для краткости в приведенном выше коде предполагается, что размер страницы составляет 4 КБ. Хотя это верно для x86 и x64, это может быть неверно для других поддерживаемых архитектур CLR. Чтобы получить размер страницы машины (размер dwPageSize структуры SYSTEM_INFO ), можно выполнить вызов GetSystemInfo .

Обратите внимание, что этот метод не особо переносим и не годится для будущего. Использование pinvoke ограничивает полезность этого подхода для хостов Windows. Предположения о непрерывности и направлении роста стека CLR могут оставаться верными для нынешних реализаций Microsoft. Однако мое (возможно, ограниченное) чтение стандарта CLI (общеязыковая инфраструктура, PDF, долгое чтение), похоже, не требует такого большого количества стеков потоков. Что касается интерфейса командной строки, каждый вызов метода требует кадра стека; однако ему все равно, если стеки растут вверх, если стеки локальных переменных отделены от стеков возвращаемых значений или если кадры стека размещаются в куче.

17
ответ дан 1 December 2019 в 03:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: