2010-05-24 48 views
10

Có cách nào để kiểm tra kích thước ngăn xếp chủ đề trong C# không?Kiểm tra kích thước ngăn xếp trong C#

+3

Tại sao bạn muốn? –

+0

Theo như tôi biết, bạn không thể. Ít nhất không sử dụng phương pháp gốc. – Propeng

+0

Tôi muốn biết số lượng ngăn xếp được sử dụng tại một thời điểm nhất định. Cho phép nói rằng tôi gọi một phương pháp đệ quy 10 lần, tôi muốn biết có bao nhiêu của stacked được sử dụng (hoặc trái) tại thời điểm đó – Gjorgji

Trả lời

14

Đây là trường hợp của if you have to ask, you can't afford it (Raymond Chen nói trước.) Nếu mã phụ thuộc vào việc có đủ không gian ngăn xếp đến mức phải kiểm tra trước, có thể đáng giá để cấu trúc lại nó để sử dụng rõ ràng Stack<T> thay vào đó. Có bằng khen trong nhận xét của John về việc sử dụng một profiler thay thế.

Điều đó nói rằng, hóa ra rằng có một cách để ước tính khoảng trống còn lại. Nó không chính xác, nhưng nó đủ hữu ích cho mục đích đánh giá mức độ gần đáy của bạn. Sau đây là rất nhiều dựa trên một excellent article by Joe Duffy.

Chúng tôi biết (hoặc sẽ làm cho các giả định) rằng:

  1. bộ nhớ stack được phân bổ trong một khối liền kề.
  2. Ngăn xếp phát triển 'xuống dưới', từ các địa chỉ cao hơn đến các địa chỉ thấp hơn.
  3. Hệ thống cần một số khoảng trống gần dưới cùng của không gian ngăn xếp được cấp phát để cho phép xử lý các ngoại lệ out-of-stack một cách duyên dáng. Chúng tôi không biết chính xác không gian dành riêng, nhưng chúng tôi sẽ cố gắng bảo thủ nó.

Với những giả định, chúng ta có thể PInvoke VirtualQuery để có được địa chỉ bắt đầu của ngăn xếp được phân bổ, và trừ nó từ địa chỉ của một số biến đống phân bổ (thu được với mã không an toàn.) Hơn nữa trừ ước tính của chúng ta về không gian hệ thống cần ở cuối ngăn xếp sẽ cho chúng ta ước tính về không gian có sẵn.

Đoạn code dưới đây chứng tỏ điều này bằng cách gọi một hàm đệ quy và viết ra không gian đống còn lại ước tính, tính bằng byte, vì nó đi:

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(); 
     } 
    } 
} 

Và đây là danh sách 10 dòng đầu tiên của đầu ra (intel x64, .NET 4.0, gỡ lỗi). Với kích thước ngăn xếp mặc định là 1MB, số lượng có vẻ hợp lý.

969332 
969256 
969180 
969104 
969028 
968952 
968876 
968800 
968724 
968648 

Để ngắn gọn, đoạn mã trên giả sử kích thước trang 4K. Trong khi điều đó đúng với x86 và x64, nó có thể không đúng cho các kiến ​​trúc CLR được hỗ trợ khác. Bạn có thể pinvoke vào GetSystemInfo để có được kích thước trang của máy (dwPageSize của cấu trúc SYSTEM_INFO).

Lưu ý rằng kỹ thuật này không đặc biệt là xách tay, cũng không phải là bằng chứng trong tương lai. Việc sử dụng pinvoke giới hạn tiện ích của phương pháp này đối với máy chủ Windows. Các giả định về tính liên tục và hướng tăng trưởng của ngăn xếp CLR có thể đúng đối với việc triển khai Microsoft hiện tại. Tuy nhiên, số đọc của tôi (có thể giới hạn) của CLI standard (cơ sở hạ tầng ngôn ngữ chung, PDF, đọc lâu) không xuất hiện để yêu cầu nhiều ngăn xếp chuỗi. Theo như CLI có liên quan, mỗi lời gọi phương thức đòi hỏi một khung ngăn xếp; nó không thể quan tâm ít hơn, tuy nhiên, nếu ngăn xếp phát triển trở lên, nếu ngăn xếp biến địa phương được tách biệt với ngăn xếp giá trị trả lại, hoặc nếu ngăn xếp khung được phân bổ trên heap.

+0

+1 để được giải thích và chi tiết. –

+1

Nếu một người yêu cầu một số liên tục, "một chương trình có thể sử dụng một cách an toàn bao nhiêu", tôi sẽ đồng ý với triết lý "IYHTA, YCAI". Mặt khác, nếu người ta viết một cái gì đó giống như trình phân tích cú pháp, nơi người ta có thể sử dụng đệ quy để xử lý bất kỳ mức độ kiến ​​trúc lồng nhau nào trên đầu vào, có vẻ sẽ sạch hơn để kiểm tra đệ quy không gian ngăn xếp còn lại và gọi ném "làm tổ quá sâu "ngoại lệ nếu nó không đầy đủ, hơn là áp đặt một số giới hạn tùy ý vào lồng nhau. – supercat

+1

Kiểm tra này cũng có thể hữu ích trong việc gỡ lỗi để đặt điểm ngắt trong trường hợp bạn đang chạy về phía ngăn xếp ngăn xếp. Điểm ngắt sẽ cho phép bạn đi đến đầu ngăn xếp cuộc gọi và kiểm tra mọi biến. Ngay sau khi StackOverflowException đã được ném, Visual Studio không thể đọc các biến nữa, đã quá muộn. – ygoe

Các vấn đề liên quan