什么是堆栈溢出错误?它很可能在哪些程序/编程语言中发生?在Web应用程序代码中发生的可能性不大吗?
什么是堆栈溢出错误?它很可能在哪些程序/编程语言中发生?在Web应用程序代码中发生的可能性不大吗?
来自维基百科:
在软件中,当调用堆栈使用过多内存时,就会发生堆栈溢出。在许多编程语言中,调用堆栈包含有限数量的内存,通常在程序开始时确定。
堆栈是一种数据结构,记录了程序子例程执行完成后应返回控制权的位置。随着调用子例程的推进,返回地址会被压入堆栈,当子例程执行完成后,返回地址将从堆栈中被弹出。如果存在许多子例程且堆栈没有足够的空间,则会发生堆栈溢出。
堆栈还用于存储局部变量,因此如果局部变量太大,则堆栈不足以存储它,这也会导致堆栈溢出。
维基百科包含了一个很好的图示,描述了当从名为DrawSquare
的另一个子例程中调用DrawLine
子例程时堆栈的情况,我希望这张图片能帮助更好地理解堆栈结构。
堆栈溢出的两个主要原因是:函数递归过深和堆栈变量过大。由于这些在几乎所有编程语言中都是常见术语,除了语言的复杂性之外,堆栈溢出也可能会发生。
Guffa贡献:堆栈与垃圾收集无关。现代应用程序具有较大的堆栈,这使得堆栈溢出的可能性稍微降低了一些,但除此之外并没有什么区别。
堆栈包含多个堆栈帧并存储在内存中。每次调用函数时,都会向堆栈添加一个新的堆栈帧。堆栈帧包含要传递给被调用函数的参数和返回地址,以便在被调用函数完成后,cpu 知道应该返回哪里,以便继续执行调用函数。堆栈帧还可以包含被调用函数的局部变量可以使用的内存。
在这个例子中,Main 函数调用了 WriteCustomerDetails,并且该函数又调用 PrintToConsole 来写出 WriteCustomerDetails 查找到的每个数据位:
'======= 堆栈顶部 ====================='
函数:PrintToConsole
参数:John Smith、34 Acacia Avenue、年龄 23
'-----------------------------------------------------------'
函数:WriteCustomerDetails
参数:John Smith
'-----------------------------------------------------------'
函数:Main
'====== 堆栈底部 ===================='
如果没有为堆栈预留足够的空间,则会发生堆栈溢出。通常,堆栈位于一个大的连续内存块中,因此不会被分成多个块,这意味着需要一个大内存块来存储它,这使运行时难以尝试增加为其保留的堆栈空间,如果它填满了。
堆栈溢出通常会在意外编写调用自身的函数时发生。有时,允许函数调用自身,只要函数中有一个 "if "或一些条件来停止调用。这称为递归函数。但是,如果没有停止并且函数不断调用自身,或者可能两个或更多函数互相调用,那么它们将非常快地耗尽所有堆栈内存。当没有剩余时,就会发生堆栈溢出并导致程序崩溃。
这种情况可能发生在任何程序中,它们不一定需要很复杂,并且可能会发生在运行网站的代码中。此外,它也可能发生在脚本语言中。
当你使用太多的堆栈空间时,就会发生堆栈溢出。通常有两种情况会导致这种情况发生:
第一种情况是代码中存在错误,导致递归循环没有退出。例如,一个属性从自身读取:
public int Length {
get {
return Length;
}
}
public bool Odd(int value) {
if (value == 0) {
return false;
} else {
return !Odd(value - 1);
}
}
当你使用堆栈(duh...)并且存在内存分配/读取问题时,就会发生堆栈溢出。在“Web程序”中,正如你所说的(我假设你是在谈论HTML、PHP、JS),要么你不使用堆栈,要么所使用的语言不允许进行低级内存控制,从而避免这些问题。
当分配给堆栈的内存耗尽时,系统将抛出堆栈溢出错误。
注意: 堆栈是一种只允许推入和弹出的内存。 您无法访问其中的值。 它遵循后进先出的原则。
当递归方法调用没有基础/终止条件时,堆栈逻辑结构过度充满时会发生堆栈溢出。在典型的程序中,您的原始变量(例如整数、浮点数等)存储在物理内存中,而您的方法调用存储在逻辑数据结构(如堆栈)中。堆栈以后进先出的顺序(LIFO)存储数据。
Main(){
foo(3);
}
foo(n){
if(n<1) // recursion with base condition that terminates when n<1
return;
else foo(n-1);
print ('Hello' + n);
}
如果没有基本条件if(n<1) return
,方法foo(n)
将递归调用自身,直到堆栈中没有更多空间,因此会出现堆栈溢出。