Android中引起内存泄露的原因分析

昨天晚上,通过Android Studio的内存分析工具Android Monitor分析到我写的一个照片选择类出现了内存泄露,还挺严重的。虽没造成oom 之类的crash,但是身为一个有代码洁癖症的程序员,并且一直对内存泄露颇有研究的我,我决定还是要找到出现内存泄露的原因,从头看代码,看了一个多小时,总算找到了。

先说说内存溢出和内存泄露的区别。

内存溢出就是oom,意思是往内存里放的数据太多了,内存装不下了,就溢出了。就像你往杯子里装水一样,杯子就那么大,你还是一直往里面装,自然会溢出。

而内存泄露呢,就是一个对象你都不用它了,但是它还是在内存里赖着不走,java虚拟机就算执行了GC(内存回收),它也一样在内存里赖着不走。就像办公室的冰箱,会有清洁工去经常清理,把不用的东西给扔掉。比如有个塑料袋装的馒头,是个已离职的同事的,确实没人要了,但是上面贴了个标签写的是:“此馒头不要扔”。而清洁工看到这个标签,以为还有人要呢,就没清理。于是这个馒头就造成了冰箱空间的泄露。

好的程序员一定要注意防止内存泄露,那么我们怎么防止内存泄露呢?

刚好在网上看到一篇文章,觉得总结的挺好的,但是原文链接已经没了,所以我就整理一下贴过来:

1、静态集合类

声明为静态(static)的HashMap、Vector 等集合类的使用最容易引起内存泄漏,因为这些静态变量的生命周期与应用程序一致,如果该HashMap是静态的,那么它将一直存在,而其中所有的Object对象也不能被释放,因为它们也将一直被该HashMap引用着。

2、 监听器(观察者模式) 
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,比如观察者模式,我们会调用一个控件的诸如addClickListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去remove这些监听器,从而增加了内存泄漏的机会。

3、物理链接,数据库操作
一些物理连接,比如数据库连接和网络连接,除非其显式的关闭了连接,否则是不会自动被GC 回收的。例如Java 数据库连接一般用DataSource.getConnection()来创建,当不再使用时必须用Close()方法来释放,因为这些连接是独立于JVM的。其中对于Resultset和Statement对象可以不进行显式回收,但Connection一定要显式回收,因为Connection在任何时候都无法自动回收,而Connection一旦回收,Resultset和Statement对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset和Statement对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。

4、内部类和外部模块等的引用 
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。对于程序员而言,自己的程序很清楚,如果发现内存泄漏,自己对这些对象的引用可以很快定位并解决,但是现在的实际开发过程中往往并非一个人实现,模块化的思想在现代软件中非常明显,所以程序员要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如: 
public void registerMsg(Object b); 
这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B 是否提供相应的操作去除引用。

Android里面最容易发生的内部类导致的内存泄露就是Handler,在Activity里面定义了一个private Handler,此时在里面引用了Activity,就很容易引起内存泄露。

我昨天晚上遇到的问题就是上面写到的第三个,是使用了数据库,但是没关闭cursor,具体代码如下,红色的框里面的关闭代码是我后来修复时加的,之前没有:


===============================
如果你觉得帮到了你,请给作者打赏一口饭吃:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值