你了解栈溢出StackOverFloweExeption的原理吗?
StackOverflowException的常见几种引起的方式
1.类的相互引用
2.方法的循环调用
3.属性Set方法的死循环调用
class Program : IProgram { IPerson iPerson = new Person();//类的相互引用 static void Main(string[] args) { Person person = new Person(); person.InfiniteRecursion(1);//方法的循环调用 person.Name = "Ryzen"; person.Age = 23; person.Sex = "男";//属性Set方法的死循环调用 Program program = new Program(); Console.WriteLine(program.iPerson.ReturnHello()); } public string RetrunWorld() { return "World"; }
}
public class Person : IPerson { IProgram iProgram = new Program(); public Person() { Console.WriteLine(iProgram.RetrunWorld()); } public int InfiniteRecursion(int a) { int b = a; return GetResult(b); } private int GetResult(int a) { int result = a + 1; return InfiniteRecursion(result); } public string ReturnHello() { return "Hello"; } #region 属性 private string _name; public string Name { get { return _name; } set { _name = value; } } private int _age; public int Age { get { return _age; } set { _age= value; } } private string _sex; public string Sex { get { return _sex; } set { Sex= value; } } #endregion }
interface IProgram { string RetrunWorld(); } interface IPerson { string ReturnHello(); }
为什么会抛出该异常(栈溢出的异常)?
一.什么是栈?
首先我们知道在一个进程当中内存主要分为两大区域:栈区和堆区,而每个进程当中又有一个或多个线程而栈区就是存在于每个一个线程当中,且每个线程存在初始化大小为1M,因此我们又把栈称为线程堆栈,或者线程栈,而且线程栈是不受GC(垃圾回收机制)管理的,从溢出两个字就可以说明栈其实是有一个空间大小的,当栈分配的空间不足以存放超过分配空间的大小的数据,就发生了栈溢出的情况下
在这里提一句解释下堆区,堆区是一个进程在初始化的时候会在内存又划出一块区域用来存储某种类型的,而且受GC管理,因此又称为托管堆而且托管堆区在32位系统在一个进程中能最多被分配1.5G的内存空间,在64位能分配8TB内存空间,因此我们很少会遇到堆的溢出,而且GC会在特定的时间释放没用到的堆空间,
首先我们知道栈是内存划分出来为一个区域专门为一个进程存放某种类型的数据,下面大概讲解栈为存放那些类型的数据
1.参数:值参数,引用参数,输出参数
2.局部变量
3.返回数据和返回地址
因此我们终于知道为什么方法的循环调用和属性的Set方法的写错(属性本质也是方法)以及类的循环调用会导致栈溢出异常的产生,因为方法的循环调用没有返回,导致在一个进程当中的栈区域一直被填充,且线程栈其实在一个进程当中是有一定的空间的,直到填充超出了栈顶,因此溢出了
二.怎么快速确定什么原因排查到错误
这时候要请出我们宇宙第一IDE:Visual Studio,在调试的时候通过调用栈堆来观看方法的调用,最顶层则是最新调用的一次方法从下图在调用栈堆清晰可以看到是两个类在初始化的时候互相调用了,调用栈堆和线程栈是一一对应的,当方法调用完后会释放线程栈,则调用栈堆也会少一条信息
文章本着学习的目的,如果文章当中有不正确的地方或者有更好的解释欢迎指点