C#报”StackOverflowException”的原因以及解决办法

  • Post category:C#

当一个递归方法的调用深度超过运行时堆栈的最大深度时,会引发 “StackOverflowException” 异常,这是 .NET 平台运行时所做的一种保护措施,以避免系统崩溃。也就是说,方法的递归调用过程中引用了太多的堆栈内存,导致堆栈溢出。

解决方法主要有两种:

  1. 更改程序的设计或算法,减少或消除递归算法的使用或减少参数或变量的使用,或减少递归深度,以避免堆栈溢出的问题。
public int ComputeFibonacci(int num)
{
    if(num <= 1) return num;
    return ComputeFibonacci(num - 1) + ComputeFibonacci(num - 2);
}

//上述方法采用递归方式求斐波那契数列,如果 num 值较大,则会引发 "StackOverflowException" 异常。可以改用迭代方式实现。
public int ComputeFibonacci(int num)
{
    if(num <= 1) return num;
    int first = 0, second = 1, result = 0;
    for(int i = 2; i <= num; i++)
    {
        result = first + second;
        first = second;
        second = result;
    }
    return result;
}
  1. 增加栈空间的大小,可以使用 “App.config” 文件来设置栈空间的大小。
<configuration>
  <runtime>
    <customErrors mode="Off"/>
    <gcAllowVeryLargeObjects enabled="true"/>
    <disableAllSecurityTypes enabled="false"/>
    <legacyUnhandledExceptionPolicy enabled="false"/>    
    <startup>
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
      <!-- 默认堆栈大小是 1MB,可以适当增大 -->
      <AppContextSwitchOverrides value="Switch.System.Runtime.Serialization.SerializationGuard.CatchExceptions=true"/>
      <AppContextSwitchOverrides value="Switch.System.Threading.Thread.Tasks.Serialization.CrossAppDomainArraySerialization=true"/>
      <AppContextSwitchOverrides value="Switch.System.Diagnostics.IgnorePortablePDBsInStackTraces=true"/>
      <AppContextSwitchOverrides value="Switch.System.Diagnostics.Tracing.ThrowOnEventWriteErrors=true"/>
      <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.Xml.UseSha256XmlEncoding=true"/>
      <AppContextSwitchOverrides value="Switch.System.Data.SqlClient.DisablePrepareOnFirstPreparedStatementExecution=false"/>
      <AppContextSwitchOverrides value="Switch.System.Data.SqlClient.EnableRetryLogic=false"/>
      <AppContextSwitchOverrides value="Switch.System.Data.SqlClient.DisableStatementPooling=false"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Authentication.SecureSocketsExtensions.NetworkingCbcRecordSplitting=true"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.AesCryptoServiceProvider.DeprecateAesManaged=false"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.EncryptedXml.UseDomForEncryptedXml=false"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.SHA256CryptoServiceProvider.AesNi=false"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.ECDiffieHellman.UseLegacyImplementation=false"/>
      <AppContextSwitchOverrides value="Switch.System.Web.Http.Cors.DisableAsyncSupport=false"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions.UseLegacyCertificateDialog=false"/>
      <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.Xml.EncryptedXmlThrowOnDuplicateId=false"/>
    </startup>
  </runtime>
</configuration>