内存问题和内存泄露详解

内存问题大致分为以下几种:

内存溢出(out of memory):

简单的说,就是内存不够用了.

内存泄露(memory leak):

简单的说,就是动态分配的内存空间没有释放.

内存抖动:

简单的说,就是gc频繁,内存一直在分配,释放.

下面重点来讲下内存泄露:

内存泄露发生的对象:

强引用的对象,不会被垃圾回收机制回。了解JVM回收机制的都知道JVM是使用引用计数法和可达性分析算法来判断对象是否是不再使用的对象,本质都是判断一个对象是否还被引用。

内存泄漏的情况:


1、静态集合类,如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

2、各种连接,如数据库连接、网络连接和IO连接等。在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。

3、变量不合理的作用域。一般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为null,很有可能导致内存泄漏的发生。public class UsingRandom {

   private String msg;

   public void receiveMsg(){

       readFromNet();// 从网络中接受数据保存到msg中

       saveDB();// 把msg保存到数据库中

}

}

如上面这个伪代码,通过readFromNet方法把接受的消息保存在变量msg中,然后调用saveDB方法把msg的内容保存到数据库中,此时msg已经就没用了,由于msg的生命周期与对象的生命周期相同,此时msg还不能回收,因此造成了内存泄漏。实际上这个msg变量可以放在receiveMsg方法内部,当方法使用完,那么msg的生命周期也就结束,此时就可以回收了。还有一种方法,在使用完msg后,把msg设置为null,这样垃圾回收器也会回收msg的内存空间。------------置null

4、内部类持有外部类,如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。-----------将内部类改为静态内部类静态内部类中使用弱引用来引用外部类的成员变量

5、改变哈希值,当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄漏。

内存泄露的原因:

java引起的内存泄露:

1.生命周期不一致导致的内存泄露

比如:

单例的生命周期一般和应用的生命周期一样长,如果一个对象已经不在使用了,而单例还持有该对象的引用,就会导致该对象无法回收,从而导致内存泄露。

静态集合类。

2.资源未关闭导致的内存泄露

数据库的连接,IO的连接。-----------关闭资源

比如:一些流,broadcastreceiver,stream,file,cursor等.

3.线程导致的内存泄露

如,AsyncTask和Runnable.线程或者异步任务如果在activity销毁之前还未完成,就会导致activity的内存资源无法被回收,从而导致内存泄露. ---------------静态内部类 + WeakReference

4.全局性的集合变量

集合类如果仅仅添加元素,而没有删除机制,将会导致内存占用只增不减.------------集合类需要有成对的添加删除或者清空机制.

为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。


c/c++引起的内存泄露:

实际上,使用C/C++这类没有垃圾回收机制的语言时,你很多时间都花在处理如何正确释放内存上。

若要确定代码中某一部分是否发生了内存泄漏,可以在该部分之前和之后对内存状态拍快照,然后使用 _CrtMemDifference 比较这两个状态:

顾名思义,_CrtMemDifference 比较两个内存状态(s1 和 s2),生成这两个状态之间差异的结果(s3)。 在程序的开始和结尾放置 _CrtMemCheckpoint 调用,并使用_CrtMemDifference 比较结果,是检查内存泄漏的另一种方法。 如果检测到泄漏,则可以使用 _CrtMemCheckpoint 调用通过二进制搜索技术_CrtMemDumpStatistics来划分程序和定位泄漏。

调用了malloc/new等内存申请的操作,但缺少了对应的free/delete.我们在编程时需要注意这点,保证每个malloc都有对应的free,每个new都有对应的deleted!!!

js引起的内存泄露:

JavaScript 是一种垃圾回收语言。垃圾回收语言通过周期性地检查先前分配的内存是否可达,帮助开发者管理内存。垃圾回收语言的内存泄漏主因是不需要的引用。

1.意外的全局变量

与全局变量相关的增加内存消耗的一个主因是缓存。缓存数据是为了重用,缓存必须有一个大小上限才有用。高内存消耗导致缓存突破上限,因为缓存内容无法被回收。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。

2.被遗忘的计时器或回调函数

3.脱离 DOM 的引用

子元素被引用,父元素无法被回收.

4.闭包

前面说过,及时清除引用非常重要.ES6 考虑到了这一点,推出了两种新的数据结构:WeakSet 和 WeakMap。

你可能感兴趣的:(内存问题和内存泄露详解)