”弱引用“”来优化使用“内部类”造成的内存溢出
来源:互联网 发布:行政审批流程优化方案 编辑:程序博客网 时间:2024/05/17 02:57
什么是内部类?什么是内存泄露?为什么Android的内部类容易引起内存泄露?如何解决?
1.什么是内部类?
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
class Circle { private double radius = 0; public Circle(double radius) { this.radius = radius; getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问 } private Draw getDrawInstance() { return new Draw(); } class Draw { //内部类 public void drawSahpe() { System.out.println(radius); //外部类的private成员 } }}
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
public class Test { public static void main(String[] args) { //第一种方式: Outter outter = new Outter(); Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建 //第二种方式: Outter.Inner inner1 = outter.getInnerInstance(); }}class Outter { private Inner inner = null; public Outter() { } public Inner getInnerInstance() { if(inner == null) inner = new Inner(); return inner; } class Inner { public Inner() { } }}
内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。比如上面的例子,如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
2.Android中的内部类使用
经常会遇见Android程序中这样使用handler:
public class SomeActivity { // ...... private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch(msg.what) { case 0: // do something break; case 1: // do something break; default: break; } } }; private void someMethod () { mHandler.sendEmptyMessage(0); }}
上述代码中,mHandler字段指向一个匿名Handler类。匿名类是内部类吗?匿名类会持有外部类的对象吗? 答案是:匿名类是内部类,但是是特殊的内部类,如果把匿名类放到一个static方法中,它是不会持有外部类实例的。而在上面的代码中,这个mHandler会持有外部类(SomeActivity)实例的引用,因为它处于一个对象的上下文中,而不是类型上下文中。
什么是”持有外部类实例的引用“?你可以这么理解:
public class InnerClass { private OuterClass outer; public InnerClass(OuterClass outer) { this.outer = outer; }}
就是说,创建InnerClass对象的时,必须传递进去一个OuterClass对象,赋值给InnerClass的一个字段outer,该字段是OuterClass对象的引用。回忆一下GC原理,如果InnerClass对象没有被标记为垃圾对象,那么outer指向的OuterClass对象会可能被标记为垃圾对象吗?答案是:InnerClass对象与GC Root有引用路径,InnerClass对象又引用了OuterClass对象,那么OuterClass对象到GC Root也是有引用路径的,所以,OuterClass不可能是垃圾对象。
3.为什么发生内存泄露?
由上文可以看出:当mHandler没有被回收时,其外围Activity对象不能被回收。当Activity被用户关闭(finish),而此时mHandler还未被回收,那么Activity对象就不会被回收,造成Activity内存泄露。
问题的关键转入到了这个问题:为什么Activity被finish了,mHandler还不能被回收?
发送消息时,我们使用了这个函数:mHandler.sendEmptyMessage(0)函数。通过查看源码追踪调用关系,发现走到了:
Message对象有个target字段,该字段是Handler类型,引用了当前Handler对象。一句话就是:你通过Handler发往消息队列的Message对象持有了Handler对象的引用。假如Message对象一直在消息队列中未被处理释放掉,你的Handler对象就不会被释放,进而你的Activity也不会被释放。
这种现象很常见,当消息队列中含有大量的Message等待处理,你发的Message需要等几秒才能被处理,而此时你关闭Activity,就会引起内存泄露。如果你经常send一些delay的消息,即使消息队列不繁忙,在delay到达之前关闭Activity也会造成内存泄露。
4.有什么解决方案?
方案#1:在关闭Activity时(finish/onStop等函数中),取消还在排队的Message:mHandler.removeCallbacksAndMessages(null);
方案#2:使用WeakReference截断StrongReference。问题的症结既然是内部类持有外部类对象的引用,那我不用内部类就行了,直接使用静态成员类。但mHandler又需要与Activity对象交互,那就来个WeakReference,指向外部Activity对象。
public class SomeActivity { private Handler mHandler = new MyHandler(this); private static class MyHandler extends Handler { private WeakReference<SomeActivity> ref; public MyHandler(SomeActivity activity) { if (activity != null) { ref = new WeakReference<SomeActivity>(activity); } } @Override public void handleMessage(Message msg) { if (ref == null) { return; } SomeActivity v = ref.get(); if (v == null) { return; } // handle message } }}
当Activity想关闭销毁时,mHandler对它的弱引用没有影响,该销毁销毁;当mHandler通过WeakReference拿不到Activity对象时,说明Activity已经销毁了,就不用处理了,相当于丢弃了消息。
5.分析工具
eclipse mat
Android studio lint
http://www.jianshu.com/p/c49f778e7acf
- ”弱引用“”来优化使用“内部类”造成的内存溢出
- 非静态内部类持有外部类的引用 使用不慎会造成内存溢出
- 匿名内部类造成的内存泄漏
- 匿名内部类持外部引用造成内存泄漏问题
- 匿名内部类持外部引用造成内存泄漏问题
- 非静态的内部类会持有外部类的一个隐式引用 造成内存泄露
- 非静态内部类造成的内存泄漏
- Android 匿名内部类造成的内存泄漏
- 关于匿名内部类,非静态内部类会造成内存泄露的隐患。
- Android之“Handler内部类引起的内存溢出警告”
- Android内存泄漏(非静态内部类创建静态实例造成的内存泄漏)
- 内存优化-内存泄漏处理内部类
- 非静态内部类创建静态实例造成的内存泄漏
- 使用内部类来实现Java的”多继承“
- 使用内部类来开发一个存放数据的容器
- 使用静态内部类来加强类的封装性
- 为何Android使用非静态内部类容易造成的泄漏!
- 内部类、匿名内部类的使用
- mybatis-初级篇-UserDaoTestByMapper
- 第五周 北理公开课的编程作业题
- Spring和SpringMVC的区别
- Unity学习记录——手牌区域的曲面化以及DOTween实现旋转
- gitlab
- ”弱引用“”来优化使用“内部类”造成的内存溢出
- 题2
- javascript中this的深度剖析
- 使用yuicompressor-maven-plugin压缩js及css文件
- 题4
- Shiro身份验证
- Excel文件自毁VBA指令
- [BZOJ][HAOI2012][背包DP]音量调节
- 当file_get_contents或者simplexml_load_file的时候乱码