谈谈你对堆栈溢出的理解?

        工作了这么多年,不论是换工作还是面试求职者,关于堆栈问题不少,和同事面试一个求职者的时候,同事出了这么一个问题考问应聘者,谈谈对堆栈溢出的理解。这个问题也是几年前某大厂考我的一个问题,印象比较深,所以在这里我就谈谈我对堆栈溢出的理解。

        首先要搞清楚什么是堆栈?

       堆栈是一个在计算机科学中经常使用的抽象数据类型。堆栈具有后进先出的特性。堆栈中定义了一些操作,其中push和pop是两个最典型的操作,push操作在堆栈的顶部加入一个元素。pop操作相反,在堆栈顶部移去一个元素,并将堆栈的大小减1。

        堆栈溢出就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据越界。常指调用堆栈溢出,本质上一种数据结构的满溢情况。堆栈溢出可以理解为两个方面:堆溢出和栈溢出。

        堆,很容易让我联想到内存堆。​所以堆溢出可以理解为内存溢出(OutOfMemory),比如不断的new 一个对象,一直创建新的对象,而不进行释放,最终导致内存超出虚拟机分配的内存空间。

        如下代码展示的内存溢出:

import java.util.ArrayList;
import java.util.List;
​public class OutOfMemoryTest {
    public static void main(String[] args) {
        List<int[]> list = new ArrayList<int[]>();
        for (; ; ) {
            int[] tmp = new int[1000000];
            list.add(tmp);
        }
    }
}

         运行报错:

        原因是for死循环一直在new整形数组并add到list中,最终导致内存不足出现异常。

        栈,很容易联想到到方法调用栈。栈溢出,可以理解为方法调用栈溢出(StackOverflow),如:一次函数调用中,栈中将被依次压入:参数,返回地址等,而方法如果递归比较深或进去死循环,就会导致栈溢出。如下:

public class StackOverflowTest {
    public static void main(String[] args) {
        method();
    }
​
    public static void method() {
        method();
    }
}

        运行报错如下:

        原因是method一直在调用自身方法,无法退出该方法,导致方法栈溢出。

解决办法:

        堆溢出(OutOfMemory),最直接的是通过增大堆内存,但demo中死循环开辟内存多大的内存也无法满足内存需要,所以优化内存分配以及及时回收内存才是解决此类异常的首先。栈溢出(StackOverflow),就是需要处理方法递归调用时正确处理递归结束的条件。

                                                

1. 堆栈溢出实例: ```c #include <stdio.h> #include <stdlib.h> void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 21; (*ret) += 8; } int main() { int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; } ``` 上述代码中,`function` 函数中声明了两个缓冲区 `buffer1` 和 `buffer2`,它们的大小分别为 5 和 10 字节。在 `function` 函数中,我们试图通过 `(*ret) += 8;` 来修改返回地址,从而实现栈溢出攻击。运行此程序时,会发现输出结果为 1,也就是攻击成功。 2. 如何在 Linux 中发现堆栈溢出问题? 在 Linux 中,我们可以使用 `gdb` 来调试程序,以检查是否存在堆栈溢出问题。具体步骤如下: - 编译程序时添加 `-g` 选项,以便在调试时可以获取更多的信息。 - 使用 `gdb` 打开可执行文件。 - 使用 `run` 命令运行程序,程序会在 `function` 函数中出现段错误并崩溃。 - 使用 `backtrace` 命令查看函数调用栈,可以看到 `function` 函数中的返回地址已经被修改。 - 使用 `info frame` 命令查看当前帧的信息,可以看到修改后的返回地址。 - 使用 `info registers` 命令查看寄存器的值,可以看到 `eip` 寄存器的值已经被修改。 - 使用 `x/20x $esp` 命令查看栈的内容,可以看到返回地址已经被修改。 通过以上步骤,我们可以发现程序存在堆栈溢出问题,并进一步分析和修复这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT_熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值