栈区和堆区是两个C++内存管理中的两个区域,而且相当重要,本文详细解释栈和堆之间的概念,内存泄漏和重复释放的原因,以及使用的注意事项。
栈区
栈区是一块固定大小的空间,从高地址向低地址延申,这块空间由操作系统维护,在使用时,定义的变量依次存入栈中,在变量超出其生命周期后,按照先进后出的顺序进行自动释放,一般来说,我们直接定义的局部变量就是存储在栈区空间的。
栈空间的示意图可用上图表示,三个变量在定义上的顺序是a、b、c,但是在最后释放时,是按照c、b、a的顺序进行释放的,和数据结构中的栈在存储特点上相同。
栈区注意事项
因为栈空间有限,所以我们要尤其注意在使用递归函数时,一定要有函数的退出条件,如果无限递归下去,则会导致栈空间存储过多内容而一直释放不掉,从而导致栈溢出,也叫做爆栈,这样会使得程序奔溃,而这样的程序在编译过程中时不会报错的,只有在运行时,系统发现栈溢出,才会终止程序,初学者往往比较难以排查这种错误,所以一般写程序时,如果可以使用循环迭代,就要尽量避免使用函数递归的方式。
堆区
堆区是一块非常灵活的区域,因为使用这块空间需要程序员自己申请和释放空间,这就使得我们可以根据自己的需求来确定每个空间的使用周期,但是由此引发的问题就是,非常容易导致内存泄漏或者重复释放的问题,这两种问题同样是编译时不会报错。
内存泄漏:这个问题是我们在使用完堆区空间时,对应的空间没有释放,使得我们在下一次使用堆区空间时,明明这块空间在逻辑上已经没用了可以重新存放其他的数据,但是在物理上,由于我们没有释放这块空间,系统默认这块空间还有用,所以对应的这块内存也不会分配给任何变量,此时如果我们将原指向这块空间的指针置空或者该指针本身已经释放,那么程序员将无法通过任何途径访问到这块空间,使得这块空间在整个程序的运行期间持续占有空间,无法归还系统,这就是内存泄漏。
说个有趣的事,不知道有没有同学和我一样,初次接触内存泄漏时很紧张,因为我们说内存泄漏会让一块空间永远无法归还内存,所以担心如果程序中频繁且大量出现内存泄漏,会让电脑的内存变得无法使用,甚至需要更换内存条。在这里可以放心的告诉大家,不会的,这里涉及到操作系统给进程分配的虚拟内存,本文不展开讲述,大概意思就是,在程序运行前,操作系统会给这个程序一块空间,在程序运行期间,所有的内存开销都在这块空间进行,在程序结束之后,系统会将这块空间整个释放,也就不会损伤实际的物理内存,但是这不代表我们就可以对内存泄漏放松警惕,如果一个程序需要运行很长的时间,比如说服务器,一个服务器可能要持续运行几个月甚至是几年,如果发生内存泄漏,随着时间的推移,也会使得服务器的效率越来越低。
重复释放:这个概念就比较好理解了,就是针对同一块堆区空间而言,重复释放了我们申请的这块空间,重复释放会让程序直接终止运行,当我们释放掉一块堆区空间后,后来的变量在申请时就可能会使用到这块空间,如果我们再次进行释放,就会导致后来的变量丢失数据,所以程序会终止。
堆区空间可以这样表示,四个指针对应了我们申请的四块空间,如果此时我们将p2释放,就会得到如下图片:
我们发现这块空间虽然被释放了,但是p2还指向这块空间,此时如果再对p2解引用,就是访问非法内存,所以我们再释放掉堆区空间之后,对应的指针也应该及时置空。
相反,如果我们只将指针置空,或者更改指向,就会导致内存泄漏:
重复释放则是:
如上图,我们释放p2指向的空间之后,p5申请空间,(图中红色为p5的空间,绿色为p2的空间) 此时如果再次释放p2的空间,会释放p5的空间,导致p5的数据丢失。
堆区注意事项
综上,我们在使用堆区时,要着重注意两点,即内存泄漏和重复释放问题,好在C++11引入了智能指针,使得我们在堆区的管理上更加方便,智能指针的使用可以参考我的上一篇文章,https://blog.csdn.net/weixin_58234579/article/details/140844702?spm=1001.2014.3001.5501
合理的使用栈区和堆区可以更方便我们的变量管理和程序的编写,以上就是关于栈区和堆区我所了解的内容,如果有不足和错误之处,请大家指正。
标签:栈区,泄漏,释放,堆区,内存,空间,这块 From: https://blog.csdn.net/weixin_58234579/article/details/140959665