写了多年代码,你会 StackOverflow 吗

Intro

准备写一个傻逼代码的系列文章,怎么写 StackOverflow 的代码,怎么写死锁代码,怎么写一个把 CPU 跑满,怎么写一个 OutOfMemory 的代码。

今天主要来看 StackOverflowStackOverlow 是一个著名的问答社区,做开发相关的应该都会知道这个网站,很多你遇到的问题都可以在这个网站上找到答案。

如何让你的代码发生 StackOverflow

在 C# 中有一个 StackOverflowException 的异常,发生 StackOverlow 也就是触发 StackOverlowException,关于 StackOverflowException 的介绍可以参考微软文档

StackOverflowException is thrown for execution stack overflow errors, typically in case of a very deep or unbounded recursion.

这里节选了一小段话, 在执行堆栈发生溢出错误的时候会抛出 StackOverflowException ,典型的案例就是一个特别深的或者没有边界的递归。

这里说明了一种典型的会发生 StackOverflowException 的示例就是递归,这种方式可能大家都知道了。

除此之外还有一种情况也会引发 StackOverflowException ,微软的文档上没有明确说明,我们知道,在 C# 中,值类型的内存分配一般是在栈上的,这个栈也就是线程栈,一个线程的栈是有限制的,这里可以出一个面试题,线程栈有多大

线程栈默认 1M,也是最大值,最大也是 1M(下次面试的时候可别不知道了哈)

详细介绍可以看文档 https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread.-ctor?view=netcore-3.1

在线程栈上分配内存不够的时候同样也会引发 StackOverflowException

所以写出 StackOverflow 代码的两种方式就有了

  • 死递归(没有边界,一直在循环调用自己)
  • 线程栈内存分配

死递归

死递归这种方式最简单了,写一个方法调用自己即可,来看一个示例:

public static void Test1()
{
Test1();
}

通常这样的代码,大家一眼就能看出来,一般也不会写这样的代码,通常写出 StackOverflow 的代码都是有参数的,参数传递有问题所以导致的 StackOverflow,比如下面这样的一个示例:

// StackOverflow
public static void Test2(int num)
{
if(num > 0)
{
Test2(num);
}
}
// work
public static void Test3(int num)
{
if(num > 0)
{
Test3(--num);
}
}

下面的代码是正常代码,上面的代码会 StackOverlow ,只是因为一行代码的失误导致的问题,这种情况更为常见一些

我之前也写过一个 StackOverflow 的代码,之前的代码也是带参数的递归,递归的参数是一个枚举,方法体里有多个枚举参数,结果在方法体递归的时候传错了参数,导致了一个死递归

线程栈内存分配

通常值类型的分配是在线程栈上的,但是我们也不会在一个线程上分配很多的值类型对象,所以通常也不会因为值类型的内存分配而导致 StackOverflowException

在 C# 7.2 之后引入了 Span (.net core 2.1 之后默认包含了 Span.netstandard2.1也包含了 Span,如果是低版本或者 .net framework 需要额外引用一个包来支持 Span),我们可以在安全代码中使用 stackalloc 来实现在线程栈上分配内存,我们要使用这个来做测试,来看下面的测试代码:

public static void Test()
{
ReadOnlySpan<byte> bytes = stackalloc byte[1024 * 1024];
Console.WriteLine($"{bytes.Length} passed"); bytes = stackalloc byte[1024 * 1024 + 1];
Console.WriteLine($"{bytes.Length} passed");
}

可以看到在超过 1M 的时候就会发生 StackOverflow

Thread MaxStackSize

如果你比较细心,使用 Thread 比较多的话,你会发现 Thread 在实例化的时候,可以在构造方法里指定一个参数来指定线程的最大栈,参考文档:https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread.-ctor?view=netcore-3.1

public Thread (System.Threading.ThreadStart start, int maxStackSize)

所以我们也可以指定一个比较小的 maxStackSize 来更容易测试出来 StackOverflow

来看一个示例:

public static void Test2()
{
var thread = new Thread(() =>
{
ReadOnlySpan<byte> bytes = stackalloc byte[16*1024+1];
Console.WriteLine($"{bytes.Length} passed"); bytes = stackalloc byte[32*1024+1];
Console.WriteLine($"{bytes.Length} passed"); bytes = stackalloc byte[256*1024+1];
Console.WriteLine($"{bytes.Length} passed");
}, 1);
thread.IsBackground = true;
thread.Start();
}

这里可以看到,我直接指定了 maxStackSize 为 1,但是实际测试下来并不是1,按微软的文档上所说,最小值是 256kb(如果我没理解错的话),但是实际测试下来并不是,没有到 256kb 就发生了 StackOverflow(有大神看到还望指导一下),上面的测试输出结果

再来看一个递归的示例:

public static void Test3()
{
var thread = new Thread(() =>
{
TestMethod(1024);
}, 1);
thread.IsBackground = true;
thread.Start();
} private static void TestMethod(int num)
{
if(num > 0)
{
num--;
TestMethod(num);
}
}

输出结果如下:

上面使用的数字也许不会在你的电脑上发生异常,你可以尝试自己调试一个临界值,或者直接设置一个比较大的值

More

StackOverflowException 不能通过 try...catch 来捕捉,当发生 StackOverflowException 时应用程序就会直接退出,通常线程栈上分配内存不够的情况基本上是不会发生的,所以对于怎么避免 StackOverflowException 我们只需要在写递归代码的时候小心一些,不要写成死循环递归就可以了。

Reference

写了多年代码,你会 StackOverflow 吗的更多相关文章

  1. 不写1行代码,在Mac上体验ASP.NET 5的最简单方法

    昨天微软发布了ASP.NET 5 beta2(详见ASP.NET 5 Beta2 发布),对ASP.NET 5的好奇心又被激发了. 今天下午在Mac OS X上体验了一下ASP.NET 5,而且借助Y ...

  2. 只写104行代码!在nopCommerce中如何实现自动生成网站地图

    表告诉我说你不知道nopCommerce是什么.它是目前.NET中最流行的完全开源网上商城,由俄罗斯的团队在2008年开始立项一直开发到现在已经是3.3版本了.代码目前托管在codeplex上,有兴趣 ...

  3. 写出优美代码的两个方式:一步到位VS迭代优化

    最近把手头这个安卓APP的所有事务性方法都写完了,有了以下体会,新手体会,老鸟轻拍   想写成优美代码的人一般都会有这样的想法: 一定要在写每一句代码,写每一个方法,构造每一个类的时候,都要记得优化: ...

  4. jQuery Validate 表单验证插件----通过name属性来关联字段来验证,改变默认的提示信息,将校验规则写到 js 代码中

    一.下载依赖包 网盘下载:https://yunpan.cn/cryvgGGAQ3DSW  访问密码 f224 二. 添加一个另外一个插件jquery.validate.messages_cn.js. ...

  5. 程序员的恶性循环:加班->没空学习->老是写同等水平代码->无法提升代码质量->老是出BUG->老是需要修改->加班->...

    程序员的恶性循环:加班->没空学习->老是写同等水平代码->无法提升代码质量->老是出BUG->老是需要修改->加班->...

  6. 将自己写的Python代码打包放到PyPI上

    如果是开源的Python代码,为了能够让大家更方便的使用,放到PyPI上也许是个非常不错的主意(PyPI:Python Package Index).刚开始我以为要将代码打包放到PyPI上是一件非常复 ...

  7. 瞧一瞧,看一看呐,用MVC+EF快速弄出一个CRUD,一行代码都不用写,真的一行代码都不用写!!!!

    瞧一瞧,看一看呐用MVC+EF快速弄出一个CRUD,一行代码都不用写,真的一行代码都不用写!!!! 现在要写的呢就是,用MVC和EF弄出一个CRUD四个页面和一个列表页面的一个快速DEMO,当然是在不 ...

  8. NASA关于如何写出安全代码的10条军规

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:NASA关于如何写出安全代码的10条军规.

  9. javascript 写一段代码,判断一个字符串中出现次数最多的字符串,并统计出现的次数

    javascript 写一段代码,判断一个字符串中出现次数最多的字符串,并统计出现的次数 function test(){ var bt = document.getElementById(" ...

随机推荐

  1. JsonAnalyzer2 1.01版

    本版的改进主要在字符串的处理,前版不允许出现[]{},:等,现在都可以了,做出的修改主要在Lexer类,另外Token类增加1了下标,TreeBuilder类的不合语法处也做出一定修改. 测试用例:h ...

  2. leetcode刷题-40组合总和2

    题目 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的每个数字在每个组合中只能使用 ...

  3. 让document.write的广告无阻塞的加载

    广告代码分析 很多第三方的广告系统都是使用document.write来加载广告,如下面的一个javascript的广告链接. 1 <script type="text/javascr ...

  4. Readme for Software engineering

    作业任务: 软件工程 软件工程 作业要求 作业要求 作业目标 博客园.github注册 自我介绍 软工5问 自我介绍: 广东工业大学计算机学院18级信息安全二班 广东工业大学AD攻防工作室成员& ...

  5. 在 Windows 上安装 Composer

    a.去官网 getcomposer.org 下载安装程序 b.运行安装程序,需要开启三个扩展 openssl.curl.mbstring,没有开启的话 composer 也可以帮助开启:会自动将com ...

  6. jzoj 6798. 【2014广州市选day2】regions

    Description 在平面上堆叠着若干矩形,这些矩形的四边与平面X坐标轴或Y坐标轴平行.下图展示了其中一种情况,3个矩形的边将平面划分成8个区域: 下面展示了另一种稍稍复杂一些的情况: 你的任务是 ...

  7. 通过调用标识符确定this

    一. 纲 this的性质 作用:表示函数执行时的环境 值:一个对象 特点:动态性 确定this的难度 JS语言的动态性: 函数的this在执行时才能确定 函数为一级公民 可作实参.返回值.数据赋值进行 ...

  8. Java 审计之SSRF篇

    Java 审计之SSRF篇 0x00 前言 本篇文章来记录一下Java SSRF的审计学习相关内容. 0x01 SSRF漏洞详解 原理: 服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过 ...

  9. matplotlib | Python强大的作图工具,让你从此驾驭图表(二)

    今天是数据处理专题的第10篇文章,我们继续来聊聊matplot这个工具库. 在上周的文章当中我们介绍了matplot的基本用法,以及展示了一些简单的例子,让大家直观地了解这个工具包.我们可以简单地将它 ...

  10. hystrix动态修改参数

    Hystrix 从入门到深入——运行时修改动态配置 /** * * @author zhangshuo * */ @Component public class DynamicConfigSource ...