2009-10-21 12 views
91

Tôi nhận được một cuộc gọi đệ quy đến một methode mà ném một ngoại lệ tràn ngăn xếp. Cuộc gọi đầu tiên được bao quanh bởi khối try try nhưng ngoại lệ không bị bắt.C# bắt một ngoại lệ tràn ngăn xếp

Ngoại lệ tràn ngăn xếp có hoạt động theo cách đặc biệt không? Tôi có thể nắm bắt/xử lý đúng ngoại lệ không?

NB: nếu có liên quan:

  • ngoại trừ không được ném vào các chủ đề chính

  • đối tượng nơi mã là ném ngoại lệ được tự nạp bởi Assembly.LoadFrom (...) .CreateInstance (...)

+3

@RichardOD, chắc chắn tôi sửa lỗi vì đó là lỗi. Tuy nhiên vấn đề có thể xuất hiện theo một cách khác và tôi sẽ xử lý nó theo một cách khác và tôi sẽ xử lý nó – Toto

+7

Đồng ý, tràn ngăn xếp là một lỗi nghiêm trọng không thể bị bắt vì nó * không * bị bắt. Sửa mã bị hỏng để thay thế. –

+9

@RichardOD: Nếu muốn thiết kế ví dụ: một trình phân tích cú pháp đệ quy và không áp đặt các giới hạn nhân tạo về chiều sâu vượt quá các giới hạn thực sự được yêu cầu bởi máy chủ, làm thế nào người ta nên đi về nó? Nếu tôi có các druthers của tôi, sẽ có một ngoại lệ StackCritical mà có thể được bắt một cách rõ ràng, mà sẽ được bắn trong khi vẫn còn một chút ngăn xếp không gian còn lại; nó sẽ vô hiệu hóa chính nó cho đến khi nó đã thực sự bị ném, và sau đó có thể không bị bắt cho đến khi một số lượng an toàn của không gian ngăn xếp vẫn còn. – supercat

Trả lời

88

Bắt đầu với 2.0 một StackOverflow Ngoại lệ chỉ có thể bị phát hiện trong các trường hợp sau đây.

  1. Các CLR đang được chạy trong một môi trường tổ chức nơi chủ nhà đặc biệt cho phép ngoại lệ StackOverflow để được xử lý
  2. Ngoại lệ stackoverflow được ném theo mã người sử dụng và không phải do một tình huống thực tế stack overflow (Reference)
+17

Nếu nó không thể bị bắt trong bất kỳ scebario có liên quan, tại sao đối tượng StackoverflowException tồn tại? – Manu

+8

@Manu cho ít nhất một vài lý do. 1) Có phải nó có thể bị bắt, loại, trong 1,1 và do đó có một mục đích. 2) Nó vẫn có thể bị bắt nếu bạn đang lưu trữ CLR vì vậy nó vẫn là một loại ngoại lệ hợp lệ – JaredPar

+2

Nếu nó không thể bị bắt ... Tại sao sự kiện cửa sổ không giải thích những gì đã xảy ra bao gồm theo dõi ngăn xếp đầy đủ theo mặc định? –

6

Có từ CLR 2.0 ngăn xếp tràn được xem là tình huống không thể khôi phục. Vì vậy, thời gian chạy vẫn đóng cửa quá trình.

Để biết chi tiết xin vui lòng xem tài liệu http://msdn.microsoft.com/en-us/library/system.stackoverflowexception.aspx

+0

Từ CLR 2.0, 'StackOverflowException' chấm dứt quy trình theo mặc định. –

+0

Không. Bạn có thể bắt OOM và trong một số trường hợp, bạn có thể làm như vậy. Tôi không biết ý bạn là gì khi sợi biến mất. Nếu một luồng có ngoại lệ chưa được xử lý thì CLR sẽ chấm dứt quá trình. Nếu thread của bạn hoàn thành phương thức của nó thì nó sẽ được dọn sạch. –

33

Từ trang MSDN trên StackOverflowException s:

Trong các phiên bản trước của .NET Framework , ứng dụng của bạn có thể bắt một đối tượng StackOverflowException (đối với ví dụ, để khôi phục từ đệ quy không bị chặn). Tuy nhiên, thực tế hiện không được khuyến khích vì mã bổ sung quan trọng là bắt buộc để nhận được ngăn xếp một cách đáng tin cậy ngoại lệ tràn và tiếp tục thực thi chương trình.

Bắt đầu với phiên bản .NET Framework 2.0, một đối tượng StackOverflowException không thể được đánh bắt bởi một khối try-catch và quá trình tương ứng là chấm dứt theo mặc định. Do đó, người dùng nên viết mã số để phát hiện và ngăn chặn ngăn xếp tràn. Ví dụ: nếu ứng dụng của bạn tùy thuộc vào đệ quy, hãy sử dụng bộ đếm hoặc điều kiện trạng thái để chấm dứt vòng lặp đệ quy. Lưu ý rằng một ứng dụng lưu trữ thời gian chạy ngôn ngữ chung (CLR) có thể chỉ định rằng CLR dỡ bỏ miền ứng dụng nơi ngăn xếp ngoại lệ tràn xảy ra và để cho quá trình tương ứng tiếp tục. Để biết thêm thông tin, hãy xem Giao diện quản lý ICLRPolicyManager và Lưu trữ Thời gian chạy ngôn ngữ chung.

5

Bạn không thể. CLR sẽ không cho phép bạn. Ngăn xếp ngăn xếp là lỗi nghiêm trọng và không thể khôi phục được.

+0

Vì vậy, làm thế nào để bạn thực hiện một bài kiểm tra đơn vị thất bại cho trường hợp ngoại lệ này nếu thay vì bị bắt nó treo các Á hậu thử nghiệm đơn vị thay thế? – BrainSlugs83

+0

@ BrainSlugs83. Bạn không, bởi vì đó là một ý tưởng ngớ ngẩn. Tại sao bạn thử nghiệm nếu mã của bạn không thành công với StackOverflowException? Điều gì xảy ra nếu CLR thay đổi để nó có thể xử lý một ngăn xếp sâu hơn? Điều gì sẽ xảy ra nếu bạn gọi cho đơn vị chức năng được thử nghiệm của bạn ở đâu đó mà đã có một chồng lồng sâu? Nó có vẻ như một cái gì đó mà không thể được kiểm tra. Nếu bạn đang cố gắng ném nó theo cách thủ công, hãy chọn một ngoại lệ tốt hơn cho tác vụ. –

3

Điều đó là không thể, và vì một lý do chính đáng (đối với một, hãy nghĩ về tất cả những điều bắt giữ đó (ngoại lệ) xung quanh).

Nếu bạn muốn tiếp tục thực hiện sau khi tràn ngăn xếp, hãy chạy mã nguy hiểm trong một AppDomain khác. Chính sách CLR có thể được thiết lập để chấm dứt hiện tại AppDomain trên tràn mà không ảnh hưởng đến tên miền gốc.

+1

Câu lệnh "bắt" sẽ không thực sự là vấn đề, bởi vì vào thời điểm một câu lệnh bắt có thể thực thi hệ thống có thể đã quay trở lại hiệu ứng của bất cứ điều gì đã cố gắng sử dụng hai không gian ngăn xếp.Không có lý do gì để bắt các trường hợp ngoại lệ tràn ngăn xếp sẽ phải nguy hiểm. Lý do chúng là ngoại lệ như vậy không thể bị bắt là cho phép chúng bị bắt một cách an toàn sẽ yêu cầu thêm một số chi phí phụ vào * tất cả * mã sử dụng ngăn xếp, ngay cả khi nó không tràn. – supercat

+3

Tại một số điểm, tuyên bố không được suy nghĩ tốt. Nếu bạn không thể bắt được Stackoverflow, bạn có thể không bao giờ biết được nó xảy ra ở đâu trong môi trường sản xuất. – Offler

20

Như nhiều người dùng đã nói, bạn không thể bắt ngoại lệ. Tuy nhiên, nếu bạn đang đấu tranh để tìm ra nơi nó đang xảy ra, bạn có thể muốn cấu hình studio trực quan để phá vỡ khi nó ném.

Để làm điều đó, bạn cần mở Cài đặt ngoại lệ từ trình đơn 'Gỡ lỗi'. Trong các phiên bản cũ của Visual Studio, đây là tại 'Debug' - 'Exceptions'; trong các phiên bản mới hơn, tại 'Debug' - 'Windows' - 'Exception Settings'.

Khi bạn đã mở cài đặt, hãy mở rộng 'Ngoại lệ thời gian chạy ngoại ngữ chung', mở rộng 'Hệ thống', cuộn xuống và chọn 'System.StackOverflowException'. Sau đó, bạn có thể nhìn vào ngăn xếp cuộc gọi và tìm kiếm mẫu lặp lại của các cuộc gọi. Điều đó sẽ cung cấp cho bạn một ý tưởng về nơi để tìm cách sửa mã mà gây ra tràn ngăn xếp.

+0

Lỗi gỡ lỗi - Ngoại lệ trong VS 2015 ở đâu? – FrenkyB

+0

Gỡ lỗi - Windows - Cài đặt Ngoại lệ – Simon

38

Cách đúng là để sửa chữa các lỗi tràn, nhưng ....

Bạn có thể cung cấp cho mình một chồng lớn hơn: -

using System.Threading; 
Thread T = new Thread(threadDelegate, stackSizeInBytes); 
T.Start(); 

Bạn có thể sử dụng tài sản System.Diagnostics.StackTrace FrameCount để đếm các khung bạn đã sử dụng và ném ngoại lệ của riêng bạn khi đạt đến giới hạn khung.

Hoặc, bạn có thể tính toán kích thước của đống còn lại và ném ngoại lệ riêng của bạn khi nó giảm xuống dưới một ngưỡng: -

class Program 
{ 
    static int n; 
    static int topOfStack; 
    const int stackSize = 1000000; // Default? 

    // The func is 76 bytes, but we need space to unwind the exception. 
    const int spaceRequired = 18*1024; 

    unsafe static void Main(string[] args) 
    { 
     int var; 
     topOfStack = (int)&var; 

     n=0; 
     recurse(); 
    } 

    unsafe static void recurse() 
    { 
     int remaining; 
     remaining = stackSize - (topOfStack - (int)&remaining); 
     if (remaining < spaceRequired) 
      throw new Exception("Cheese"); 
     n++; 
     recurse(); 
    } 
} 

Chỉ cần bắt Cheese. ;)

+34

'Phô mai 'cách xa cụ thể. Tôi muốn đi cho 'ném mới CheeseException ("Gouda"), ' –

+12

@ C.Evenhuis Trong khi không có nghi ngờ rằng Gouda là một pho mát đặc biệt nó phải là một RollingCheeseException (" Double Gloucester ") thực sự thấy http: // www. cheese-rolling.co.uk/ –

+2

lol, 1) fixing là không thể vì không bắt nó, bạn thường không biết nó xảy ra ở đâu 2) tăng Stacksize là vô ích với ** vô tận ** đệ quy andm 3) kiểm tra Stack ở đúng vị trí giống như – Firo

5

Bạn có thể không phải là hầu hết các bài viết đều được giải thích, cho tôi thêm một khu vực khác:

Trên nhiều trang web mà bạn sẽ tìm thấy những người nói rằng cách để tránh điều này là sử dụng một AppDomain khác nhau vì vậy nếu đây xảy ra miền sẽ được tải xuống. Đó là hoàn toàn sai (trừ khi bạn lưu trữ CLR của bạn) như là hành vi mặc định của CLR sẽ nâng cao một sự kiện KillProcess, đưa xuống AppDomain mặc định của bạn.

7

Như đã đề cập ở trên nhiều lần, bạn không thể bắt được StackOverflowException do Hệ thống do trạng thái quá trình bị hỏng gây ra. Nhưng có một cách để chú ý đến ngoại lệ như một sự kiện:

http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx

Bắt đầu với phiên bản .NET Framework 4, sự kiện này không được đưa lên cho trường hợp ngoại lệ mà tham nhũng trạng thái của quá trình, chẳng hạn như ngăn xếp tràn hoặc truy cập vi phạm, trừ khi xử lý sự kiện là quan trọng về bảo mật và có thuộc tính HandleProcessCorruptedStateExceptionsAttribute.

Tuy nhiên, ứng dụng của bạn sẽ chấm dứt sau khi thoát khỏi chức năng sự kiện (giải pháp thay thế rất bẩn) là khởi động lại ứng dụng trong sự kiện này haha, không làm như vậy và sẽ không bao giờ làm). Nhưng nó đủ tốt để đăng nhập!

Trong .NET Framework phiên bản 1.0 và 1.1, một ngoại lệ chưa được xử lý xảy ra trong một chuỗi khác với luồng ứng dụng chính bị bộ đếm ghi lại và do đó không khiến ứng dụng chấm dứt. Vì vậy, có thể cho sự kiện UnhandledException được nâng lên mà không có ứng dụng chấm dứt. Bắt đầu với .NET Framework phiên bản 2.0, backstop cho các ngoại lệ chưa được giải quyết trong các chủ đề con đã bị loại bỏ, vì hiệu ứng tích lũy của các lỗi im lặng đó bao gồm suy giảm hiệu suất, dữ liệu bị hỏng và khóa, tất cả đều khó gỡ lỗi. Để biết thêm thông tin, bao gồm danh sách các trường hợp mà thời gian chạy không chấm dứt, hãy xem Ngoại lệ trong Chủ đề được Quản lý.

+0

wow. điều này sẽ có nhiều upvotes hơn! –