通过Stack Overflow线程栈溢出的问题实例,详解C++程序线程栈溢出的诸多细节

news2025/1/21 18:55:31

目录

1、问题说明

2、从Visual Studio输出窗口中找到了线索,发生了Stack Overflow线程栈溢出的异常

3、发生Stack Overflow线程栈溢出的原因分析

4、线程占用的栈空间大小说明

5、引发线程栈溢出的常见原因和场景总结

6、在问题函数入口处添加return语句,在Debug下运行,还是会发生线程栈溢出异常

7、在问题函数入口处添加return,到release下运行就不报线程栈溢出的异常了

8、如何查看函数入口处分配栈内存的汇编代码?

9、最后


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战进阶(已更新到380多篇,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585Windows C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12695902.htmlC++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       今天通过项目中遇到的一个线程栈溢出的实例,详细讲解线程栈溢出问题的排查过程以及涉及的诸多细节,以供大家借鉴或参考。通过Stack Overflow线程栈溢出的问题实例,详解C++程序线程栈溢出的诸多细节

1、问题说明

        前段时间同事那边的代码出了问题,他为了实现某个新需求,在现有的代码中添加了一个业务消息,底层的业务模块将消息抛给上层模块,在上层模块中添加了该消息的响应函数去处理该消息,编译代码后运行,程序就直接闪退了。

       同事排查分析了很久,始终找不到问题,于是找到我,让我帮忙分析一下。他说将新加的消息处理函数注释掉,就不会有崩溃,放开代码后,崩溃就是必现的。基本可以确定是新加的消息处理函数有问题,但同事一直没找到原因。

2、从Visual Studio输出窗口中找到了线索,发生了Stack Overflow线程栈溢出的异常

        我到同事那边后,他复现了问题,打开函数调用堆栈页面,看程序崩溃在一个不相关的模块中。从现有的堆栈,看不到出问题的接口。切换到Output输出窗口中,看到了输出窗口的打印,看到了Stack overflow线程栈溢出的打印

联想到是新加的消息处理函数后出现的,估计是新加的消息处理函数中引发了线程栈溢出的问题。

       于是让同事打开新加的消息处理函数的代码:

一眼就看出来问题,用TInstantConference_Api结构体定义了一个局部变量:

TMtInstantConference_Api tInstanceConfInfo;

这个结构体我们以前也经常用,该结构体的成员很多,定义的很大很复杂。用该结构体定义的局部变量占用的就是栈空间,会占用很大的栈空间,导致所在线程占用的总的栈空间超过了分配给线程的栈空间的上限,引发了Stack overflow线程栈溢出的异常。

3、发生Stack Overflow线程栈溢出的原因分析

       在程序中添加以下的测试代码:

int nSize = sizeof(TMtInstantConference_Api);

看看这个结构体的大小。打断点调试运行,上述代码返回的该结构体的大小为1096432/1024/1024 = 1.04MB。

       在Windows系统中,系统给每个线程分配的默认栈内存大小是1MB,而此处直接使用这个TInstantConference_Api结构体直接定义一个局部变量,光这个局部变量占用的栈内存就达到了1.04MB,就超过了所在线程的1MB的栈内存的上限,所以产生了Stack overflow线程栈溢出的异常。

4、线程占用的栈空间大小说明

       线程是系统分配栈空间的基本单元,即栈空间是分配给线程使用的。函数中的局部变量会占用栈内存,函数调用时传递给被调用函数的参数占用的内存空间也是栈内存。

在函数调用时,主调函数可能会通过栈将要传递给被调用函数的参数内存中的值压到栈上(栈内存),传递给被调用函数。这点在32位程序中比较常见,但在64位程序中因为64位寄存器比较多,就直接使用寄存器传递了,寄存器传递相对栈内存传递,效率会高一些。当然传递的参数比较多,或者参数内存比较大时,也会使用栈内存传递参数。

       线程在某一时刻占用的栈空间的实际大小,是当前线程的函数调用堆栈中所有函数占用的栈空间之和,如果总和超过了系统分配给当前线程的栈空间上限,就会引发Stack Overflow线程栈溢出的异常,进而导致程序发生崩溃。

在Windows系统中,系统给线程分配的默认栈内存大小是1MB。在Linux系统中,系统给线程分配的默认栈内存大小是8MB,可以在Linux系统中使用ulimit命令查看:

       这里涉及到C++程序在运行时所占用的内存分区,一般可分为栈内存区、堆内存区、全局/静态内存区、文字常量内存区及程序代码区5大分区。关于C++程序的内存分区,可以查看我的文章:

实例详解C++程序的五大内存分区https://blog.csdn.net/chenlycly/article/details/120958761

5、引发线程栈溢出的常见原因和场景总结

       引发线程栈溢出问题可能有以下几个可能

1)函数递归调用的深度过深
因为一直在递归调用,在到达最底下的那层调用之前,递归函数一直没返回,栈空间一直没有释放,导致当前线程占用的栈空间越来越多,达到上限。
2)消息上触发函数的死循环调用
消息触发的函数死循环调用,因为死循环调用了,函数的栈空间一直没释放,导致当前线程占用的栈空间越来越多。这个问题我们在实际项目中遇到过两次。
3)定义了一个占用内存很大的局部变量
比如定义了一个很庞大的结构体,在一个函数中用该结构体定义了一个局部变量,假设该结构体接近或者大于1MB,则会直接导致线程栈溢出。
4)函数中使用switch...case语句,包含了大量的case分支
每个case分支中都定义了局部变量,导致当前函数占用了大量的栈空间。case分支中的局部变量的生命周期是在case分支中的,即代码运行到对应的case分支中时该分支中的局部变量才有“生命”,但其实这个局部变量的栈空间已经在函数入口处分配好栈空间了,并不是代码执行到case子句中才分配栈空间的。这点可以通过编写测试代码,查看函数入口处给当前函数分配栈空间的汇编代码就能看出来了,可以先顶一个变量查看汇编代码看看分配了多少栈空间,然后再增加一个变量,看看分配的栈空间是否变大。
5)多个if-else分支,每个分支中都有定义局部变量
引发问题的原因与多个case语句的原因是类似的,此处就不再赘述了。

       上述问题场景我在项目中都遇到过,我也是通过项目遇到的问题总结出上述场景的。实践出真知,大家要养成多思考多总结的习惯,这对提升个人技术水平、积累实践经验是很有用处的!

       之前也排查过一个典型的线程栈内存溢出问题,感兴趣的话,可以去查看我之前写的文章:

线程栈溢出异常,程序崩溃在汇编代码test dword ptr [eax],eax上的问题排查icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131743305

6、在问题函数入口处添加return语句,在Debug下运行,还是会发生线程栈溢出异常

       既然问题出在新加的消息处理函数中,同事尝试直接在该函数的入口处添加一句return语句:

直接将当前消息处理函数return掉。然后再次测试,还是会出现闪退,依旧是Stack overflow线程栈溢出的异常。

       其实依然出问题的原因很简单,可以查看该函数的汇编代码,在函数的入口处的汇编代码中我们可以看到给当前函数分配栈空间的汇编语句:(随便找个函数,在函数入口处设置断点,命中断点后,鼠标右键点击断点附近的代码,在弹出的右键菜单中点击“转到反汇编”去查看汇编代码上下文即可看到)

这句汇编代码在return之前,并且是编译时就确定加到二进制文件中的。

       所以,即使在函数入口处加了一句return,但函数入口处的汇编还是分配了栈空间,即还没执行到return这句代码时就已经给当前函数分配了栈内存,所以还是会导致Stack overflow线程栈溢出的异常。因为还没执行到问题函数的内部,在校验当前线程栈空间的代码时就出现了线程栈溢出的异常,所以函数调用堆栈中看不到该函数的调用。


        在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:(该精品技术专栏的订阅量已达到480多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2:  

C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795

常用的C++软件辅助分析工具有PE工具、Dependency Walker、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!

专栏3:(本专栏涵盖了多方面的内容,是当前重点打造的专栏,专栏文章已经更新到380多篇,持续更新中...)

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。

专栏4:   

VC++常用功能开发汇总(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585

将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。

专栏5: 

Windows C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12695902.html

根据多年C++软件开发实践,详细地总结了Windows C++ 应用软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。


7、在问题函数入口处添加return,到release下运行就不报线程栈溢出的异常了

       同事特意测试了一下,保留函数入口处的return语句:(也可以在工程设置中,在Debug下开启优化,当然一般不这么设置,此处是为了验证问题)

编译release版本,然后跑release版本并没有发生Stack overflow线程栈溢出的异常。也让我帮忙看看这是啥原因。

       其实原因很简单,想想应该就知道了,是Release下的优化起的作用。因为Debug下为了调试将优化关闭了,而Release下是开启优化的,在Release下编译器发现函数入口处就return了,就直接把return后面的代码都优化掉了,只保留return语句,所以生成的二进制文件中就不会分配那么大的栈内存了,就不会有线程栈溢出的异常了。

8、如何查看函数入口处分配栈内存的汇编代码?

       随便找一个函数,在函数开始处打上一个断点:

然后开启调试运行,当命中断点时,直接右键点击断点处,在弹出的右键菜单中点击“转到反汇编”菜单项:

即可查看对应的汇编代码,给函数分配栈空间的代码如下:

系统给线程分配的1MB的栈空间,当前线程中的函数会占用这一栈空间,这里涉及到ebp和esp两个寄存器,ebp用来存放当前函数的栈基址,esp用来存放当前函数的栈顶地址。

       栈内存是从大地址向小地址使用的,对于被调用函数,其栈基址就是主调函数的栈顶地址,保存到ebp中,即mov ebp, esp(可以看上面汇编截图中的入口处)。然后在被调用函数中,在该被调用函数入口处的esp,就是主调函数的栈顶地址,减去一个数值,同时将减的结果赋值给当前的esp。这样被调函数占用的栈空间范围就是当前函数的esp到ebp中的范围,其中esp < ebp。

       这里结合函数调用时的栈分布更好理解,关于函数调用时的栈分布,可以查看我的文章:

C++函数调用栈分布详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/121001096

9、最后

       本文通过一个Stack Overflow线程栈溢出的问题实战分析实例,详细讲解了线程栈溢出涉及到的诸多细节,有一定的参考价值,希望对大家能有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1978438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

How dose age change how you leanr?(1)年龄如何影响学习能力?(一)

As you get older, learning often feels harder than it used to.Why is that?What changes in the brain as we age that makes acquire new information harder? Is there anything we can do to avoid our minds slowing down? 随着年龄增长&#xff0c;常常会觉得学习…

Vue+live2d实现虚拟人物互动(一次体验叙述)

目录 故事的开头&#xff1a; 最终的实现效果&#xff1a; 实现步骤&#xff1a; 第一步&#xff1a;下载重要文件 第二步&#xff1a;创建vue项目文件&#xff0c;将刚下载文件拷贝到public目录下 第三步&#xff1a;在index.html文件中引入js 第四步&#xff1a;使用&…

SAM 2:Segment Anything in Images and Videos 论文详解

SAM 2:Segment Anything in Images and Videos 文章目录 SAM 2:Segment Anything in Images and Videos摘要1 Introduction具体分析: 2 Related work具体分析&#xff1a; 3 任务&#xff1a;可提示的视觉分割4 模型具体分析&#xff1a; 5 数据5.1 Data engine5 . 2 SA - V数据…

c++ 初始值设定项列表(initializer_list)

引例 我们在写c代码的时候&#xff0c;多多少少会遇到这样写的&#xff1a; 如果是这样写还好说&#xff1a; 第一个是因为编译器强制匹配参数。 其他都是因为在有对应构造函数的情况下支持的隐式类型转换。 而支持的构造函数是这个&#xff1a; 如果有不懂的可以开这一篇&a…

致远互联FE协作办公平台apprvaddNew接口SQL注入漏洞复现 [附POC]

文章目录 致远互联FE协作办公平台apprvaddNew接口SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现致远互联FE协作办公平台apprvaddNew接口SQL注入漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用文章内…

BGP对等体组、聚合、路由反射器、联盟、团体属性

一.实验拓扑 二.实验需求 1.AS1中存在两个环回&#xff0c;一个地址为192.168.1.0/24&#xff0c;该地址不能在任何协议中宣告 As3中存在两个环回&#xff0c;一个地址为192.168.2.0/24,、该地址不能在任何协议中宣告&#xff0c;最终要求这两个环回可以ping通; 2.整个AS2的I…

【数据结构】栈和队列(c语言实现)(附源码)

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;数据结构 目录 一、栈 1.栈的概念与结构 2.栈的实现 2.1 栈的结构定义 2.2 方法的声明 2.3 方法的实现 2.3.1 初始化 2.3.2 销毁 2.3.3 判空 2.3.4 压…

OpenCV||超详细的图像边缘检测

一、基本概念 1.图像边缘检测目的 特征提取&#xff1a;边缘是图像中亮度变化最显著的部分&#xff0c;它们通常对应于物体的轮廓、不同区域的边界等。通过边缘检测&#xff0c;可以从图像中提取出这些重要的特征信息&#xff0c;为后续处理如图像分割、目标识别等提供基础。 …

请你学习:前端布局3 - 浮动 float

1 标准流&#xff08;也称为普通流、文档流&#xff09; 标准流&#xff08;也称为普通流、文档流&#xff09;是CSS中元素布局的基础方式&#xff0c;它决定了元素在页面上的默认排列方式。这种布局方式遵循HTML文档的结构&#xff0c;不需要额外的CSS样式来指定元素的位置。…

python open cv(图像处理的基本操作)

概要图 1读取图像 cv2.imread() 函数是OpenCV库中用于读取图像文件的函数。它有两个参数&#xff1a; 文件名&#xff1a;这是第一个也是必需的参数&#xff0c;它指定了要读取的图像文件的路径和文件名。这个路径可以是相对路径&#xff0c;也可以是绝对路径。 标志&#xf…

小技巧大功效,「仅阅读两次提示」让循环语言模型超越Transformer++

在当前 AI 领域&#xff0c;大语言模型采用的主流架构是 Transformer。不过&#xff0c;随着 RWKV、Mamba 等架构的陆续问世&#xff0c;出现了一个很明显的趋势&#xff1a;在语言建模困惑度方面与 Transformer 较量的循环大语言模型正在快速进入人们的视线。 令人兴奋的是&am…

c++ string解析及其实现

因为字符串是固定长度&#xff0c;不好进行操作&#xff0c;因此c就用类将字符串进行了封装让其变得方便实用。 要深刻了解string&#xff0c;我们必须要熟练掌握类的使用&#xff0c;如果还有疑问可以看这一篇博客:c 类 (要学习类这一篇就够了 ) string #include<string…

Android 11(R)启动流程 初版

启动流程 bootloader会去启动android第一个进程Idle&#xff0c;pid为0&#xff0c;会对进程 内存管理等进行初始化。Idle还被称作swapper。Idle会去创建两个进程&#xff0c;一个是init&#xff0c;另外一个是kthread。 kthread会去启动内核&#xff0c;用户是由init进行启动。…

算法通关:017_1:二叉树及三种顺序的递归遍历

文章目录 题目思路代码运行结果 题目 二叉树及三种顺序的递归遍历 思路 代码 /*** Author: ggdpzhk* CreateTime: 2024-08-04** 二叉树及三种顺序的递归遍历* LeetCode 144. 二叉树的前序遍历* LeetCode 94. 二叉树的中序遍历* LeetCode 145. 二叉树的后序遍历* LeetCode 10…

sqli-labs靶场——第二关

1、判断注入类型 ?id1和?id2-1的页面一样所以是数字型 2、判断闭合类型 数字型没有闭合符号 3、order by查看有几列 当输入order by 4 时候页面变化&#xff0c;3的时候正常&#xff0c;所以是3列 4、union select联合查询查看回显 /sqli-labs/Less-2/?id-1 union sel…

ESP32使用MQTT协议通讯(EMQX)

一、背景介绍 前面完成了ESP32MicroPython环境的搭建01_ESP32 MicroPython开发环境搭建_eps32开发板-CSDN博客 现在想实现以下功能&#xff1a; 1.通过手机或电脑&#xff0c;远程给ESP32发送相关指令。 2.ESP32接到指令后&#xff0c;做出相应的高低电平输出。 这样就相当…

视频编辑SDK,底层架构合理,前端自定义程度高

如何高效、专业地制作出符合品牌形象、吸引目标受众的视频内容&#xff0c;成为了众多企业面临的共同挑战。美摄科技&#xff0c;作为视频编辑技术的先行者&#xff0c;以其卓越的视频编辑SDK&#xff08;Software Development Kit&#xff09;&#xff0c;为企业用户量身打造了…

进程状态都有哪些?

目录 前言&#xff1a; 进程的各个状态&#xff1a; 1、R状态&#xff08;进程运行状态&#xff09;和S状态&#xff08;休眠状态&#xff09; 2、T状态和t状态&#xff08;暂停进程&#xff09; 3、D状态&#xff08;磁盘休眠状态&#xff09; 4、Z状态&#xff08;僵尸状…

学习笔记 韩顺平 零基础30天学会Java(2024.8.2)

P447 五大运行时异常 P448 异常课堂练习 P449 异常处理机制 try-catch-finally throws(处理机制二选一)&#xff0c;如果没有显式处理异常&#xff0c;默认throws JVM处理异常直接输出异常信息&#xff0c;退出程序 P450 tryCatch 对于第一个细节&#xff0c;发生异常之后时try…

C语言快速入门及精通学习指南——手把手教零基础/新手入门(完整C语言学习笔记整理)

前言 作为一名拥有多年开发经验的码农&#xff0c;我的职业生涯涵盖了多种编程语言&#xff0c;包括 C 语言、C、C# 和 JavaScript。在这一过程中&#xff0c;我深刻地意识到扎实的基础对于编程学习的重要性&#xff0c;尤其是对于 C 语言这样一门核心语言来说。 出于对…