C++ 内存泄漏

1. 内存泄漏:

在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。

内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

C/C++由于灵活、高效的优点一直以来都是主流的程序设计语言之一,但是其没有垃圾回收机制,内存的分配与释放均由程序员自己管理,当由于疏忽或错误造成程序未能释放不再使用的内存时就会造成内存泄漏。

在大型、复杂的应用程序中,内存泄漏往往是最常见的问题,因而及时解决内存泄漏非常必要,tMemMonitor (TMM)作为一个专业、准确、易用的内存泄漏分析工具,可以帮助C/C++程序员迅速地解决内存泄漏这个令人头疼的问题。

2. 内存泄漏后果:

只发生一次的小的内存泄漏可能不会被注意,但泄漏大量内存的程序或泄漏日益增多的程序可能会表现出各种征兆:从性能不良(并且逐渐降低)到内存完全用尽。

更糟的是,泄漏的程序可能会用掉太多内存,以致另一个程序失败,而使用户无从查找问题的真正根源。 此外,即使无害的内存泄漏也可能是其他问题的征兆。

内存泄漏会因为减少可用内存的数量从而降低计算机的性能。

内存泄漏也会导致较严重的后果:

  • 程序运行后置之不理,并且随着时间的流失消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理);
  • 新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时;
  • 程序能够请求未被释放的内存(比如共享内存),甚至是在程序终止的时候;
  • 泄漏在操作系统内部发生;
  • 泄漏在系统关键驱动中发生;
  • 内存非常有限,比如在嵌入式系统或便携设备中;
  • 当运行于一个终止时内存并不自动释放的操作系统(比如AmigaOS)之上,而且一旦丢失只能通过重启来恢复。

3. 内存泄漏的几种情况:

1). 在类的构造函数和析构函数中没有匹配的调用new和delete函数

两种情况下会出现这种内存泄露:

一是在堆里创建了对象占用了内存,但是没有显示地释放对象占用的内存;

二是在类的构造函数中动态的分配了内存,但是在析构函数中没有释放内存或者没有正确的释放内存;

2). 没有正确的清除嵌套对象的指针

3). 在释放对象数组时在delete中没有使用方括号

方括号是告诉编译器这个指针指向的是一个对象数组,同时也告诉编译器正确的对象地址值病调用对象的析构函数,如果没有方括号,那么这个指针就被默认为只指向一个对象,对象数组中的其他对象的析构函数就不会被调用,结果造成了内存泄露。

如果在方括号中间放了一个比对象数组大小还大的数字,那么编译器就会调用无效对象(内存溢出)的析构函数,会造成堆的奔溃。如果方括号中间的数字值比对象数组的大小小的话,编译器就不能调用足够多个析构函数,结果会造成内存泄露。

释放单个对象、单个基本数据类型的变量或者是基本数据类型的数组不需要大小参数,释放定义了析构函数的对象数组才需要大小参数。

4). 指向对象的指针数组不等同于对象数组

对象数组是指:数组中存放的是对象,只需要delete []p,即可调用对象数组中的每个对象的析构函数释放空间;

指向对象的指针数组是指:数组中存放的是指向对象的指针,不仅要释放每个对象的空间,还要释放每个指针的空间,delete []p只是释放了每个指针,但是并没有释放对象的空间,正确的做法,是通过一个循环,将每个对象释放了,然后再把指针释放了;

5). 缺少拷贝构造函数

两次释放相同的内存是一种错误的做法,同时可能会造成堆的崩溃。

按值传递会调用(拷贝)构造函数,引用传递不会调用。

在C++中,如果没有定义拷贝构造函数,那么编译器就会调用默认的拷贝构造函数,会逐个成员拷贝的方式来复制数据成员,如果是以逐个成员拷贝的方式来复制指针被定义为将一个变量的地址赋给另一个变量。

这种隐式的指针复制结果就是两个对象拥有指向同一个动态分配的内存空间的指针:

当释放第一个对象的时候,它的析构函数就会释放与该对象有关的动态分配的内存空间。

而释放第二个对象的时候,它的析构函数会释放相同的内存,这样是错误的。

所以,如果一个类里面有指针成员变量,要么必须显示的写拷贝构造函数和重载赋值运算符,要么禁用拷贝构造函数和重载赋值运算符

6). 缺少重载赋值运算符

这种问题跟上述问题类似,也是逐个成员拷贝的方式复制对象,如果这个类的大小是可变的,那么结果就是造成内存泄露;

7. 关于nonmodifying运算符重载的常见迷思

a. 返回栈上对象的引用或者指针(也即返回局部对象的引用或者指针)。导致最后返回的是一个空引用或者空指针,因此变成野指针;

b. 返回内部静态对象的引用;

c. 返回一个泄露内存的动态分配的对象。导致内存泄露,并且无法回收;

解决这一类问题的办法是重载运算符函数的返回值不是类型的引用,二应该是类型的返回值,即不是 int&而是int;

8. 没有将基类的析构函数定义为虚函数

当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露;

附注:

C++资源管理机制:RAII

野指针:指向被释放的或者访问受限内存的指针。

造成野指针的原因:

1. 指针变量没有被初始化(如果值不定,可以初始化为NULL);

2. 指针被free或者delete后,没有置为NULL, free和delete只是把指针所指向的内存给释放掉,并没有把指针本身干掉,此时指针指向的是“垃圾”内存。释放后的指针应该被置为NULL;

3. 指针操作超越了变量的作用范围,比如返回指向栈内存的指针就是野指针;

其实内存泄漏的原因可以概括为:调用了malloc/new等内存申请的操作,但缺少了对应的free/delete释放操作,总之就是,malloc/new比free/delete的数量多。

内存用完,不再使用要及时释放。

时间: 2025-01-08 22:17:13

C++ 内存泄漏的相关文章

检查内存泄漏

1.分配空间 2.记录内存块信息 3.调用构造函数(类型萃取) #include<iostream> #include<string> #include<list> #include<assert.h> using namespace std; struct BlockInfo { void* _ptr; string _file; int _line; BlockInfo(void *ptr, const char*file, int line) :_pt

内存泄漏工具VLD1.0_要点分析

0X01 关闭FPO优化 // Frame pointer omission (FPO) optimization should be turned off for this // entire file. The release VLD libs don't include FPO debug information, so FPO // optimization will interfere with stack walking. #pragma optimize ("y", of

内存泄漏-Node

内存泄漏: 1.缓存 2.队列消费不及时 3.作用域未释放 缓存: 必须要有过期策略 1.缓存限制策略 limitablemap LRU 2.缓存解决方案 进程自身不存储状态,进程外缓存 1)能减少常驻内存的对象的数量,让垃圾回收更高效 2)进程之间可以共享缓存 常用的缓存: Redis Memcached 内存泄漏-Node,布布扣,bubuko.com

只运行一个实例以及内存泄漏检测

unit 使应用程序只运行一个实例; interface uses Windows; const  // - 互斥体唯一的名字  _Mutex_Name = '{19631971-1976-1981-1989-199319941995}'; var  _Mutex_Handle: THandle; implementation initialization // - 载入时调用的代码 // - 创建互斥体对象_Mutex_Handle := CreateMutex(nil, False, LPC

SGI STL内存配置器存在内存泄漏吗?

阅读了SGI的源码后对STL很是膜拜,很高质量的源码,从中学到了很多.温故而知新!下文中所有STL如无特殊说明均指SGI版本实现. STL 内存配置器 STL对内存管理最核心部分我觉得是其将C++对象创建过程分解为构造.析构和内存分配.释放两类操作分离开来!摆脱了对频繁调用new或malloc函数想操作系统申请空间而造成的低效.其中析构操作时对具有non-trival.trival 析构函数的class区别对待也提高了效率.SGI 的两级配置器结构属于锦上添花. STL内存配置器有没有内存泄漏?

Android开发 |常见的内存泄漏问题及解决办法

在Android开发中,内存泄漏是比较常见的问题,有过一些Android编程经历的童鞋应该都遇到过,但为什么会出现内存泄漏呢?内存泄漏又有什么影响呢? 在Android程序开发中,当一个对象已经不需要再使用了,本该被回收时,而另外一个正在使用的对象持有它的引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了. 内存泄漏有什么影响呢?它是造成应用程序OOM的主要原因之一.由于Android系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时

(转)从内存管 理、内存泄漏、内存回收探讨C++内存管理

http://www.cr173.com/html/18898_all.html 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对 C++的痛恨,但内存管理在C++中无处不在,内存泄漏几乎在每个C++程序中都会发生,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃 C++,转到Java或者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,还放弃了C++超绝的性能

Java中的内存泄漏

[转]介绍Java中的内存泄漏 1. 什么是内存泄漏? 内存泄漏的定义:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着. 要想理解这个定义,我们需要先了解一下对象在内存中的状态.下面的这张图就解释了什么是无用对象以及什么是未被引用对象. 2. 为什么会发生内存泄漏? 来先看看下面的例子,为什么会发生内存泄漏.下面这个例子中,A对象引用B对象,A对象的生命周期(t1-t4)比B对象的生命周期(t2-t3)长的多.当B对象没有被应用程序使用之后,A对象仍然在引用着B对象.

并发编程(四):ThreadLocal从源码分析总结到内存泄漏

一.目录 1.ThreadLocal是什么?有什么用? 2.ThreadLocal源码简要总结? 3.ThreadLocal为什么会导致内存泄漏? 二.ThreadLocal是什么?有什么用? 引入话题:在并发条件下,如何正确获得共享数据?举例:假设有多个用户需要获取用户信息,一个线程对应一个用户.在mybatis中,session用于操作数据库,那么设置.获取操作分别是session.set().session.get(),如何保证每个线程都能正确操作达到想要的结果? /** * 回顾sync

LeakCanary:简单粗暴的内存泄漏检測工具

差点儿每一个程序猿在开发的过程中都会遇到内存泄漏.那么我们怎样检測到app是否哪里出现内存泄漏呢?square公司推出了一款简单粗暴的检測内存泄漏的工具-- LeakCanary 什么是内存泄漏? 内存泄漏是指因为疏忽或者错误造成程序未能释放已经不再使用的内存,内存泄漏不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误失去了对于这段内存的控制.因而造成内存的浪费. 内存泄漏和内存溢出是两码事,不要混淆,内存溢出通俗的讲就是内存不够用,如今的仅仅能手机内存越来越大,内存溢出的情况不