关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。
未经允许不得转载
一、导读
我们继续总结学习Java基础知识,温故知新。
二、概览
要学习内存泄漏,我们要知道一些基础知识,如Java引用分类:
2.1 强引用
平时常用的引用类型,JVM发生OOM也不会回收这部分引用。
如果强引用对象不使用时,需要弱化从而使GC能够回收,如 object = null;
2.2 软引用(SoftReference)
发生OOM前(jvm内存不足时)会回收这部分引用,如果想使用缓存,可以使用 LruCache,而不是SoftReference
如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;
如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
if(JVM内存不足) {
// 将软引用中的对象引用置为null
str = null;
// 通知垃圾回收器进行回收
System.gc();
}
2.3 弱引用(WeakReference)
发生GC就会回收。
一旦开始gc,只要发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
我们一起来看看构造方法
public WeakReference(T referent) {
super(referent);
}
/**
* 当GC回收对象时,将引用对象回收而将被引用对象放入ReferenceQueue
*/
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
假设我们以后对象存储在一个列表,比如HashMap中,我们想要真正的回收一个对象,仅仅把它的强引用赋值为null是不够的,还要把相应的条目从HashMap中移除。
2.4 虚引用(PhantomReference)
get方法返回null,不能获取值。
三、相关知识
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。
3.1 内存泄露危害
- 当应用程序长时间连续运行时,性能严重下降;
- 抛出OutOfMemoryError异常;
- 程序莫名其妙的自动崩溃;
- 应用程序耗尽链接对象。
- 等等
3.2 列举实际开发中遇到的内存泄露场景
- 非静态内部类的静态实例
- 单例造成的内存泄漏
如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。
- 非静态内部类创建静态实例造成的内存泄漏
eg: Handler造成的内存泄漏
原因:在类中将 Handler 声明成非静态内部类或者匿名内部类,这样Handle默认持有外部类Activity的引用,如果Activity在销毁时,
Handler还有未执行完或者正在执行的Message,而Handler又持有Activity的引用,导致GC无法回收Activity,导致内存泄漏
解决方法: 将Handler类独立出来或者使用静态内部类 + 弱引用,这样便可以避免内存泄漏。 内部类改为静态的之后,
它所引用的对象或属性也必须是静态的,所以静态内部类无法获得外部对象的引用,只能从 JVM 的 Method Area(方法区)获取到static类型的引用
问题:内部类为什么会持有外部类的引用
内部类虽然和外部类写在同一个文件中,但是编译后还是会生成不同的class文件,其中内部类的构造函数中会传入外部类的实例,
内部类持有外部类的对象的引用,是以“this$0”这个字段来保存的
Java 语言中,非静态内部类的主要作用有两个:
1. 当内部类只在外部类中使用时,匿名内部类可以让外部不知道它的存在,从而减少了代码的维护工作。
2. 当内部类持有外部类时,它就可以直接使用外部类中的变量了,这样可以很方便的完成调用,
- 线程造成的内存泄漏
ThreadLocal 造成的内存泄漏,ThreadLocal会复制变量副本,
解决方法: 将AsyncTask和Runnable类独立出来或者使用静态内部类,这样便可以避免内存泄漏。
匿名内部类/非静态内部类和异步线程。
- 资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。 资源型对象未关闭: Cursor,File
- 类的静态变量持有大数据对象(static 字段)
- 集合容器中的对象没清理造成的内存泄漏
- finalize()
重写finalize()方法时,该类的对象不会立即被垃圾收集器收集,如果finalize()方法的代码有问题,那么会潜在的引发OOM;
- 注册对象未销毁: 广播,回调监听
- WebView: 使用单独进程
3.3 软引用 & 弱引用区别
- 弱引用的对象拥有更短暂的生命周期
- 回收的时机不同
四、 推荐阅读
[SQL 专栏]
[数据结构与算法]
[Android学习专栏]
未经允许不得转载