C # bắt một ngoại lệ tràn ngăn xếp


115

Tôi có một cuộc gọi đệ quy tới một phương thức ném ngoại lệ tràn ngăn xếp. Cuộc gọi đầu tiên được bao quanh bởi một khối try catch nhưng không bắt được ngoại lệ.

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

Không chắc nếu có liên quan, nhưng thông tin bổ sung:

  • ngoại lệ không được đưa vào chuỗi chính

  • đối tượng mà mã đang ném ngoại lệ được tải thủ công bởi Assembly.LoadFrom (...). CreateInstance (...)


3
@RichardOD, chắc chắn rằng tôi đã sửa lỗi vì nó là một lỗi. Tuy nhiên vấn đề này có thể xuất hiện theo một cách khác và tôi wan để 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ắt được vì không nên bắt. Thay vào đó, hãy sửa mã bị hỏng.
Ian Kemp 21/10/09

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

2
Câu hỏi này rất hữu ích - tôi muốn thất bại một bài kiểm tra đơn vị nếu xảy ra ngoại lệ tràn ngăn xếp - nhưng NUnit chỉ chuyển bài kiểm tra sang danh mục "bị bỏ qua" thay vì không đạt như các trường hợp ngoại lệ khác - tôi cần phải nắm bắt nó và thực hiện một Assert.Failthay thế. Thật nghiêm túc - chúng ta làm thế nào để giải quyết vấn đề này?
BrainSlugs83

Câu trả lời:


109

Bắt đầu với 2.0, một Ngoại lệ StackOverflow chỉ có thể gặp phải trong các trường hợp sau.

  1. CLR đang được chạy trong môi trường được lưu trữ * nơi máy chủ lưu trữ đặc biệt cho phép xử lý các ngoại lệ của StackOverflow
  2. Ngoại lệ stackoverflow được ném bởi mã người dùng và không phải do tình huống tràn ngăn xếp thực tế ( Tham khảo )

* "môi trường được lưu trữ" như trong "mã của tôi lưu trữ CLR và tôi định cấu hình các tùy chọn của CLR" chứ không phải "mã của tôi chạy trên lưu trữ được chia sẻ"


27
Nếu nó không thể được bắt trong bất kỳ bản quyền nào có liên quan, tại sao đối tượng StackoverflowException lại tồn tại?
Manu

9
@Manu vì ít nhất một vài lý do. 1) 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 do đó, nó vẫn còn là một loại ngoại lệ hợp lệ
JaredPar

3
Nếu không thể bắt được ... Tại sao sự kiện windows giải thích những gì đã xảy ra không bao gồm dấu vết ngăn xếp đầy đủ theo mặc định?

10
Làm cách nào để cho phép StackOverflowExceptions được xử lý trong môi trường được lưu trữ? Lý do tôi hỏi là vì tôi chạy một môi trường được lưu trữ và tôi đang gặp sự cố chính xác này, nơi nó phá hủy toàn bộ nhóm ứng dụng. Tôi muốn nó hủy bỏ luồng, nơi nó có thể quay ngược trở lại đầu trang và sau đó tôi có thể ghi lại lỗi và tiếp tục mà không cần phải giết tất cả các luồng của apppool.
Brain2000

Starting with 2.0 ..., Tôi rất tò mò, điều gì đang ngăn họ bắt SO và làm thế nào nó có thể xảy ra 1.1(bạn đã đề cập đến điều đó trong nhận xét của mình)?
M.kazem Akhgary

47

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

Bạn có thể tạo cho mình một ngăn xếp lớn hơn: -

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

Bạn có thể sử dụng thuộc tính System.Diagnostics.StackTrace FrameCount để đếm số khung hình bạn đã sử dụng và đưa ra ngoại lệ của riêng bạn khi đạt đến giới hạn khung hình.

Hoặc, bạn có thể tính toán kích thước của ngăn xếp còn lại và đưa ra ngoại lệ của riêng bạn khi nó giảm xuống dưới 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. ;)


47
Cheeselà xa cụ thể. Tôi sẽ đi chothrow new CheeseException("Gouda");
C.Evenhuis

13
@ 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 cheese-rolling.co.uk

3
lol, 1) không thể sửa chữa vì nếu không nắm bắt được nó, bạn thường không biết nó xảy ra ở đâu 2) tăng Kích thước ngăn xếp là vô ích với đệ quy vô tận vàm 3) kiểm tra Ngăn xếp ở đúng vị trí giống như lần đầu tiên
Firo 12/02

2
nhưng tôi không dung nạp lactose
redoc

39

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 (ví dụ: để khôi phục từ đệ quy không bị ràng buộc). Tuy nhiên, phương pháp đó hiện không được khuyến khích vì cần có mã bổ sung đáng kể để bắt ngoại lệ tràn ngăn xếp một cách đáng tin cậy và tiếp tục thực thi chương trình.

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


23

Như một số người dùng đã nói, bạn không thể bắt được 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 định cấu hình studio trực quan để phá vỡ khi nó được 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ũ hơn của Visual Studio, đây là "Gỡ lỗi" - "Ngoại lệ"; trong các phiên bản mới hơn, nó nằm ở 'Debug' - 'Windows' - 'Exception Settings'.

Khi bạn đã mở cài đặt, hãy mở rộng 'Ngoại lệ thời gian chạy ngôn 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 kiểu lặp lại của các cuộc gọi. Điều đó sẽ cung cấp cho bạn ý tưởng về nơi cần tìm để sửa mã gây ra tràn ngăn xếp.


1
Debug - Exceptions trong VS 2015 ở đâu?
FrenkyB

1
Gỡ lỗi - Windows - Cài đặt ngoại lệ
Simon

15

Như đã đề cập ở trên vài lần, không thể bắt được StackOverflowException được Hệ thống nâng lên do trạng thái quy trình bị hỏng. Nhưng có một cách để nhận thấy 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 .NET Framework phiên bản 4, sự kiện này không được đưa ra cho các trường hợp ngoại lệ làm hỏng trạng thái của quá trình, chẳng hạn như tràn ngăn xếp hoặc vi phạm quyền truy cập, trừ khi trình xử lý sự kiện là bảo mật quan trọng và có thuộc tính HandleProcessCorrupStateExceptionsAttribute.

Tuy nhiên, ứng dụng của bạn sẽ kết thúc sau khi thoát khỏi chức năng sự kiện (một cách giải quyết 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 để ghi nhật ký!

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ông phải là chuỗi ứng dụng chính bị thời gian chạy bắt và do đó không khiến ứng dụng chấm dứt. Do đó, có thể xảy ra sự kiện UnhandledException mà ứng dụng không kết thúc. Bắt đầu với .NET Framework phiên bản 2.0, lỗi hỗ trợ này cho các trường hợp ngoại lệ chưa được khắc phục trong các luồng con đã bị loại bỏ, vì tác động tích lũy của các lỗi im lặng như vậy bao gồm giảm hiệu suất, dữ liệu bị hỏng và khóa, tất cả đều rất khó gỡ lỗi. Để biết thêm thông tin, bao gồm danh sách các trường hợp thời gian chạy không kết thúc, hãy xem Ngoại lệ trong Chủ đề được quản lý.


6

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

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


Từ CLR 2.0, một StackOverflowExceptionkết thúc quá trình theo mặc định.
Brian Rasmussen

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

5

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


Vì vậy, làm thế nào để bạn thực hiện một Unit Test không thành công cho ngoại lệ này nếu thay vì có thể bắt được nó lại làm hỏng trình chạy unit test?
BrainSlugs83

1
@ BrainSlugs83. Bạn không, bởi vì đó là một ý tưởng ngớ ngẩn. Tại sao bạn lại kiểm tra 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ì xảy ra nếu bạn gọi hàm được kiểm tra đơn vị của mình ở đâu đó đã có một ngăn xếp lồng nhau sâu sắc? Có vẻ như một thứ gì đó không thể kiểm tra được. 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ụ.
Matthew Scharley

5

Bạn không thể vì hầu hết các bài viết đang giải thích, hãy để tôi thêm một lĩnh vực khác:

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


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 được (Ngoại lệ) {} xung quanh).

Nếu bạn muốn tiếp tục thực thi 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 đặt để chấm dứt AppDomain hiện tại khi bị tràn mà không ảnh hưởng đến miền gốc.


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

4
Tại một số điểm, tuyên bố không được suy nghĩ tốt. Nếu bạn không thể nắm bắt Stackoverflow, bạn có thể không bao giờ biết nó xảy ra ở đâu trong môi trường sản xuất.
Ưu đãi vào
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.