如何避免堆栈溢出?

发布于 2024-08-05 19:18:49 字数 919 浏览 4 评论 0原文

我使用 CSharpCodeProvider 编译代码,并在结果程序集中动态创建某个类的实例。比我调用一些方法。如果该方法有递归,我会得到 StackOverflowException 并且我的应用程序终止。

我该如何避免这种情况?

using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public void Run()
            {
                Run();
            }
        }

        static void Main(string[] args)
        {
        AppDomain domain = AppDomain.CreateDomain("new");

        ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
        if (handle != null)
        {
            StackOver stack = (StackOver) handle.Unwrap();
            stack.Run();
        }

    }
}
}

有关的:

什么是堆栈溢出?

I compile my code using CSharpCodeProvider, and dynamically create instance of some class in result assembly. Than I call some method. If the method has recursion I get StackOverflowException and my app terminates.

How do I avoid this?

using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public void Run()
            {
                Run();
            }
        }

        static void Main(string[] args)
        {
        AppDomain domain = AppDomain.CreateDomain("new");

        ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
        if (handle != null)
        {
            StackOver stack = (StackOver) handle.Unwrap();
            stack.Run();
        }

    }
}
}

Related:

What is a stack overflow?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

昵称有卵用 2024-08-12 19:18:49

StackOverflow 表明您的递归太深并且堆栈内存不足。例如:

public class StackOver
{
    public void Run() 
    { 
        Run(); 
    }
}

这将导致堆栈溢出,因为 StackOver::Run() 将被一遍又一遍地调用,直到没有剩余内存为止。

我怀疑在你的情况下,你可能缺少终止条件或者你运行了太多的递归迭代。

如果您尝试保持应用程序运行,请尝试:

namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public bool Run()
            {
                return true; // Keep the application running. (Return false to quit)
            }
        }

        static void Main(string[] args)
        {
            // Other code...

            while (stack.Run());
        }

    }
}

StackOverflow indicates that your recursion is going too deep and the stack is running out of memory. For example:

public class StackOver
{
    public void Run() 
    { 
        Run(); 
    }
}

This will result in a stack overflow because StackOver::Run() will be called over and over until there is no memory left.

I suspect in your case, you may be missing a termination condition or you are running too many recursion iterations.

If you are trying to keep the application running, try:

namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public bool Run()
            {
                return true; // Keep the application running. (Return false to quit)
            }
        }

        static void Main(string[] args)
        {
            // Other code...

            while (stack.Run());
        }

    }
}
转角预定愛 2024-08-12 19:18:49

Run正在调用Run。这就是无限递归。

    class StackOver : MarshalByRefObject
    {
        public void Run()
        {
            Run(); // Recursive call with no termination
        }
    }

Run is calling Run. That is the infinite recursion.

    class StackOver : MarshalByRefObject
    {
        public void Run()
        {
            Run(); // Recursive call with no termination
        }
    }
糖果控 2024-08-12 19:18:49

如果递归导致堆栈溢出,那么问题与编译类无关——递归函数需要终止条件,因为 C#(通常)不会优化尾部调用

If recursion causes a stack overflow, then the problem is not related to compiling the class -- a recursive function needs a terminating condition, because C# doesn't (usually) optimize tail calls.

怪我闹别瞎闹 2024-08-12 19:18:49

避免递归函数堆栈溢出的唯一方法是有一个明确的退出条件,无论输入如何,最终都会满足该条件。您可以定义最大深度并在达到该深度后停止进行递归调用,或者确保您检查的数据是有限的(并且在合理的限制内),或者两者的组合。

The only way to avoid stack overflows with recursive functions is to have a clear exit condition that will eventually be met regardless of the input. Either you define a maximum depth and stop making recursive calls once you reach it, or you make sure that the data that you examine is finite (and within reasonable limits), or a combination of both.

自控 2024-08-12 19:18:49

每次从方法 bar 调用方法 foo 时,bar 都会添加到调用堆栈中。调用堆栈用于跟踪调用方法之前代码所在的位置,以便在 foo 完成时可以返回那里。

以下递归函数

int Factorial(int n) {
    if (n == 0) { return 1; }
    return n * Factorial(n - 1);
}

在多次递归调用 Factorial(5) 后,调用堆栈将如下所示:

Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1)

此时 n 为 1,因此函数停止调用递归情况并返回 1。然后程序开始回溯调用堆栈,整个过程返回 120。

如果没有调用堆栈,程序在执行完方法后将不知道要返回到哪里。

现在假设基本情况不存在,它看起来像这样:

int Factorial(int n) {
    return n * Factorial(n - 1);
}

在调用 Factorial(5) 的几次递归之后,调用堆栈将如下所示:

Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1) -> Factorial(0) -> Factorial(-1) -> Factorial(-2) -> Factorial(-3) -> Factorial(-4) -> Factorial(-5) -> Factorial(-6) -> Factorial(-7) -> Factorial(-8) -> Factorial(-9) -> Factorial(-10) -> Factorial(-11) -> Factorial(-12) -> Factorial(-13) -> Factorial(-14) -> Factorial(-15) etc…

因为代码没有停止调用自身的点,所以它会一直这样下去,调用堆栈会越来越大,占用越来越多的内存,直到超过分配的内存,并抛出 StackOverflow 异常。

有两种方法可以阻止这种情况发生,最好的方法取决于具体情况。

1 提供基本案例。确保最终达到某个条件,阻止函数调用自身。在阶乘的情况下,n == 1,但也可能是已经过去了一定的时间,已经递归了一定的次数,某些计算的某些结果在某些范围内,等等。只要在堆栈太大之前停止回收即可。

2 删除递归并重新编写它。任何递归算法都可以重写为非递归算法。它可能不那么干净和优雅,但它是可以做到的。在阶乘参数中,它可能类似于:

int Factorial(int n) {
    int result = 1;

    for (int i = 0; i < n; i += 1) {
        result *= n;
    }

    return result;
}

如果目标是一次又一次地不断运行相同的函数,那么您可以将递归重写

void Foo() {
    // Some code
    Foo();
}

void Foo() {
    while (true) { // Some code }
}

Every time you call a method foo from method bar, bar is added to the call stack. The call stack is used to keep track of where the code was before the method was called so it can return there when foo is done.

The following recursive function

int Factorial(int n) {
    if (n == 0) { return 1; }
    return n * Factorial(n - 1);
}

after several recursions of the call Factorial(5) the call stack would look like this:

Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1)

At this point n is 1, and so the function stops calling the recursive case and instead returns 1. The program then starts winding back up the call stack and the whole thing returns 120.

Without the call stack the program wouldn't know where to go back to when it had finished executing a method.

Now suppose that base case wasn't there, and it was just looked like this:

int Factorial(int n) {
    return n * Factorial(n - 1);
}

After several recursions of the call Factorial(5) the call stack would look like this:

Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1) -> Factorial(0) -> Factorial(-1) -> Factorial(-2) -> Factorial(-3) -> Factorial(-4) -> Factorial(-5) -> Factorial(-6) -> Factorial(-7) -> Factorial(-8) -> Factorial(-9) -> Factorial(-10) -> Factorial(-11) -> Factorial(-12) -> Factorial(-13) -> Factorial(-14) -> Factorial(-15) etc…

Because there is no point at which the code stops calling itself it will carry on forever, and the call stack will grow and grow and grow taking up more and more memory until it exceeds the memory it has been allocated and the StackOverflow exception is thrown.

There are 2 ways to stop this from happening, the best depends on the situation.

1 Provide a base case. Make sure there is some condition that is eventually reached which stops the function from calling itself. In the Factorial case it's that n == 1, but it could be that a certain amount of time has passed, that it has recursed a certain number of times, that some result of some computation is within some bounds, whatever. As long as it stops recusing before the stack is too big.

2 Remove the recursion and re-write it without. Any recursive algorithm can be re-written as a non-recursive algorithm. It may not be as clean and elegant, but it can be done. In the factorial argument it may be something like:

int Factorial(int n) {
    int result = 1;

    for (int i = 0; i < n; i += 1) {
        result *= n;
    }

    return result;
}

If the aim is to continually run the same function again and again, then you can re-write the recursive

void Foo() {
    // Some code
    Foo();
}

as

void Foo() {
    while (true) { // Some code }
}
谈情不如逗狗 2024-08-12 19:18:49

我没有很好的 CSharpCodeProvider 背景,但我知道递归实现的算法可以用循环来实现

I dont have a good background of CSharpCodeProvider but I know that recursively implemented algorithm could be implemented with a loop

一个人的旅程 2024-08-12 19:18:49

好的。使用或不使用 CSharpCodeProvider 并不重要。我正在另一个域中使用反射加载程序集。我认为域是出于安全原因而创建的。我怎样才能防止应用程序终止???

using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public void Run()
            {
                Run();
            }
        }

        static void Main(string[] args)
        {
        AppDomain domain = AppDomain.CreateDomain("new");

        ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
        if (handle != null)
        {
            StackOver stack = (StackOver) handle.Unwrap();
            stack.Run();
        }

    }
}
}

Ok. It doesn't matter use CSharpCodeProvider or not. I'm loading assembly using Reflection in another domain. I think domains was created for security reason. How can I safe the app from terminating???

using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public void Run()
            {
                Run();
            }
        }

        static void Main(string[] args)
        {
        AppDomain domain = AppDomain.CreateDomain("new");

        ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
        if (handle != null)
        {
            StackOver stack = (StackOver) handle.Unwrap();
            stack.Run();
        }

    }
}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文