析构函数为啥能释放对象内存?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了析构函数为啥能释放对象内存?相关的知识,希望对你有一定的参考价值。

析构函数里什么操作语句也没有,为什么就能释放对象内存呢?

析构函数(destructor) 与构造函数相反,,构造函数是新建对象时自动调用,而析构函数则是当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。
析构函数里什么操作语句也没有,为什么就能释放对象内存呢? 这话有问题,应该是在推出前用delete释放内存,不能自动释放
参考技术A 析构函数不能释放对象内存,只是析构函数中可以释放一些这个对象所占有的资源(包括内存),这是要靠你自己写程序完成的,不能自动完成,而C++在需要收回这个对象所占有的内存的时候会调用析构函数,但是C++自己是不会收回该对象申请的动态内存的,也就是new出来的内存,所以一般要考程序员自己在析构函数中完成释放。 参考技术B 关于析构函数的说明:
1.当程序的执行离开实例化自动对象所在的作用域时,自动对象就会撤销,这时析构函数隐式调用.并不是说在main函数结束时才执行。
2.析构函数本身并不释放对象占用的内存空间,它只是在系统收回对象的内存空间之前执行扫尾工作.析构函数体内并不一定要有delete语句。可以有也可以没
3.和构造函数一样,每个类都有一个析构函数,即使没有显式提供一个析构函数,编译器也会生成一个空的析构函数 .

内存泄漏简析

C++内存泄露常见情况:

1. 在类的构造函数和析构函数中没有匹配的调用newdelete函数 :  

           两种情况下会出现这种内存泄露:一是在堆里创建了对象占用了内存,但是没有显示地释放对象占用的内存;二是在类的构造函数中动态的分配了内存,但是在析构函数中没有释放内存或者没有正确的释放内存;

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

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

           方括号是告诉编译器这个指针指向的是一个对象数组,同时也告诉编译器正确的对象地址值病调用对象的析构函数,如果没有方括号,那么这个指针就被默认为只指向一个对象,对象数组中的其他对象的析构函数就不会被调用,结果造成了内存泄露。如果在方括号中间放了一个比对象数组大小还大的数字,那么编译器就会调用无效对象(内存溢出)的析构函数,会造成堆的奔溃。如果方括号中间的数字值比对象数组的大小小的话,编译器就不能调用足够多个析构函数,结果会造成内存泄露。

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

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

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

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

5. 缺少拷贝构造函数

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

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

C++中,如果没有定义拷贝构造函数,那么编译器就会调用默认的拷贝构造函数,会逐个成员拷贝的方式来复制数据成员,如果是以逐个成员拷贝的方式来复制指针被定义为将一个变量的地址赋给另一个变量。这种隐式的指针复制结果就是两个对象拥有指向同一个动态分配的内存空间的指针。当释放第一个对象的时候,它的析构函数就会释放与该对象有关的动态分配的内存空间。而释放第二个对象的时候,它的析构函数会释放相同的内存,这样是错误的。

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

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

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

 

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

造成野指针的原因:

  1. 指针变量没有被初始化(如果值不定,可以初始化为NULL
  2. 指针被free或者delete后,没有置为NULL, freedelete只是把指针所指向的内存给释放掉,并没有把指针本身干掉,此时指针指向的是“垃圾”内存。释放后的指针应该被置为NULL.
  3. 指针操作超越了变量的作用范围,比如返回指向栈内存的指针就是野指针

http://blog.csdn.net/lovely20085901/article/details/39050085

 

java内存泄露常见情况

Java存在内存泄露
我们必须先承认这个,才可以接着讨论。虽然Java存在内存泄露,但是基本上不用很关心它,特别是那些对代码本身就不讲究的就更不要去关心这个了。
Java中的内存泄露当然是指:存在无用但是垃圾回收器无法回收的对象。
而且即使有内存泄露问题存在,也不一定会表现出来。
4。Java中参数都是传值的。
对于基本类型,大家基本上没有异议,但是对于引用类型我们也不能有异议。
 

Java内存泄露情况
 
1、堆内存溢出(outOfMemoryError:java heap space)
       在jvm规范中,堆中的内存是用来生成对象实例和数组的。
       如果细分,堆内存还可以分为年轻代和年老代,年轻代包括一个eden区和两个survivor区。
       当生成新对象时,内存的申请过程如下:
          a、jvm先尝试在eden区分配新建对象所需的内存;
          b、如果内存大小足够,申请结束,否则下一步;
          c、jvm启动youngGC,试图将eden区中不活跃的对象释放掉,释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
          d、Survivor区被用来作为Eden及old的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
          e、 当OLD区空间不够时,JVM会在OLD区进行full GC;
          f、full GC后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”:

1
outOfMemoryError:java heap space

2、方法区内存溢出(outOfMemoryError:permgem space)
       在jvm规范中,方法区主要存放的是类信息、常量、静态变量等。
       所以如果程序加载的类过多,或者使用反射、gclib等这种动态代理生成类的技术,就可能导致该区发生内存溢出,一般该区发生内存溢出时的错误信息为:

1
outOfMemoryError:permgem space

3、线程栈溢出(java。lang。StackOverflowError)
       线程栈时线程独有的一块内存结构,所以线程栈发生问题必定是某个线程运行时产生的错误。
       一般线程栈溢出是由于递归太深或方法调用层级过多导致的。
       发生栈溢出的错误信息为:

1
java。lang。StackOverflowError

内存泄露几种场景:
 
1、长生命周期的对象持有短生命周期对象的引用
 
            这是内存泄露最常见的场景,也是代码设计中经常出现的问题。
            例如:在全局静态map中缓存局部变量,且没有清空操作,随着时间的推移,这个map会越来越大,造成内存泄露。
 
2、修改hashset中对象的参数值,且参数是计算哈希值的字段
 
             当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段,否则对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,造成内存泄露。
 
3、机器的连接数和关闭时间设置
 
            长时间开启非常耗费资源的连接,也会造成内存泄露。

 

http://www.jb51.net/article/73676.htm

 

以上是关于析构函数为啥能释放对象内存?的主要内容,如果未能解决你的问题,请参考以下文章

c++单例模式为啥不在析构函数中释放静态的单例对象,而要加一个内嵌类

析构函数

关于结构体中内存的释放问题

析构函数

C#析构函数

C++中基类的析构函数为什么要用virtual虚析构函数