Android内存泄漏研究
来源:互联网 发布:淘宝上aj正品店铺 编辑:程序博客网 时间:2024/05/17 23:06
概念
1、堆内存溢出。无法回收的对象达到了最大堆容量限制后,在创建对象,申请内存。就对oom。
2、直接内存溢出(native层);使用的是系统内存。如果是直接malloc或jdk自带的native方法直接调用的是malloc(这种方法都是不对使用者开放的)直接申请内 存,统内存不足,在去申请,就会直接oom。
如果是通过jdk提供的方法申请内存,抛出oom,他并没有真正的向系统分配内存,是通过计算得出内存无法分配,手动抛出异常。(这时候可以捕获异常,是释放内存)。
这种oom,是因为GC没有去回收这些内存。(没有触发GC)。
导致溢出的关键是因为 在 直接内存 申请内存 ,在垃圾回收进行时,虽然虚拟机会对 直接内存 进行回收。但是 直接内存 不能像,新生代,老年代那样,发现内存控件不足了就通知收集器进行垃圾回收,他只能等待 老年代 满了后 Full GC,然后 帮他清理掉内存的废气对象。 所以,明明堆内存有空间,也会oom。(这也是3.0之后把bitmap的放到了堆中)。
3、方法区内存溢出。 存储的是虚拟机加载的类信息,常量,静态变量、即时编译后的代码等数据。回收不了的信息沾满了就会oom。
客户端 动态加载的类,都存在这里,占用的空间也不容忽视。而且现在做的,加载了之后就不管了,很有可能会导致溢出。
我认为应该添加处理类卸载的功能。能够让GC回收它。
4、虚拟机栈和方法栈溢出.
所以产生oom原因。大家也了解了。 还是应该从代码入手。或者了解第三方的实现,能够很好的管理。
根搜索算法
Android虚拟机的垃圾回收采用的是根搜索算法
。GC会从根节点(GC Roots)开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。
根搜索算法相比引用计数法很好的解决了循环引用的问题。举个例子,Activity有View的引用,View也有Activity的引用,之前我还尝试去源代码里找Activity何时和View断开连接是大错特错了。当Activity finish掉之后,Activity和View的循环引用已成孤岛,不再引用到GC Roots,无需断开也会被回收掉。
内存泄漏
Android内存泄漏
指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。
场景
- 类的静态变量持有大数据对象
静态变量长期维持到大数据对象的引用,阻止垃圾回收。 - 非静态内部类的静态实例
非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。 - 资源对象未关闭
资源性对象如Cursor、File、Socket,应该在使用后及时关闭。未在finally中关闭,会导致异常情况下资源对象未被释放的隐患。 - 注册对象未反注册
未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。 Handler
临时性内存泄露
Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。
由于AsyncTask内部也是Handler机制,同样存在内存泄漏的风险。
此种内存泄露,一般是临时性的。
预防
- 不要维持到Activity的长久引用,对activity的引用应该和activity本身有相同的生命周期。
- 尽量使用
context-application
代替context-activity
- Activity中尽量不要使用非静态内部类,可以使用静态内部类和
WeakReference
代替。
检测
静态检测
静态检测主要是检测资源未关闭的情况,Eclipse和Android Studio都可以检测出IO或者Socket未关闭的情况,然后在finally中关闭即可。
动态监测
动态检测主要是依靠MAT这个工具。2011年Google IO有一个主题演讲,非常详细地讲解了内存泄露的检测,包含MAT工具的使用,值得一看。
我在某项目中使用MAT检测,发现一处内存泄漏,分享一下过程。
从首页到商户列表到商户详情再退回首页执行Dump HPROF File
,查看MAT中的Histogram
,过滤Activity后结果如下:
仍然存在ShopInfoActivity
的实例,选中右键点击Merge Shortest Paths to GC Roots
,结果如下:
可以看到ShopDatabase
中维持着ShopInfoActivity
的引用,查看源代码如下:
很明显,静态变量
public class ShopDatabase {
…
private static ShopDatabase instance;
public static ShopDatabase getInstance(Context context) {
if (instance == null && context != null) {
instance = new ShopDatabase(context);
}
return instance;
}
protected Context context;
…
}
instance
长期持有context
的引用,造成内存泄露。所以动态检测内存泄露的一个简单思路就是随意操作APP,最后返回首页,然后用MAT检测,查看是否存在Activity多于一个或者Activity不正常存在的问题。
参考资料
- Avoiding memory leaks
- Memory Analysis for Android Applications
- memory_management_for_android_apps
- Google IO:Android内存管理主题演讲记录
- Android内存泄漏分析及调试
- Android内存泄漏研究
- Android内存泄漏研究
- Android内存泄漏研究
- Android内存泄漏研究
- Android内存泄漏研究
- 关于android内存泄漏的研究
- 内存泄漏问题研究
- 【内存泄漏】Android内存泄漏---单例内存泄漏
- 【内存泄漏】Android内存泄漏---Handler
- Android 内存泄漏调试
- Android 内存泄漏调试
- Android 内存泄漏调试
- Android 内存泄漏调试
- Android 内存泄漏调试
- Android内存泄漏简介
- Android 内存泄漏调试
- Android 内存泄漏调试
- Android 避免内存泄漏
- JAVA IO流(2)
- 快速排序(Java)
- 基于Jenkins+git+gradle的android持续集成
- 1094. The Largest Generation
- js原生实现FastClick事件
- Android内存泄漏研究
- 由pcap文件提取IPv6的flow数据
- 使用确定对话框来控制循环
- App 从Windows到OS X的移植
- select @@identity
- 能力风暴机器人AS-MF2011循迹算法
- mysql双主复制
- 第三方登入和分享
- 邻接表(数组实现)