有时在Activity中使用Handler时会提示一个内存泄漏的警告,代码通常如下:

  1. public class MainActivity extends Activity {
  2. private TextView tvHelloWorld;
  3. private Button btnSetText;
  4. private Handler mHandler = new Handler();
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. btnSetText = (Button) findViewById(R.id.btn_set_text);
  10. tvHelloWorld = (TextView) findViewById(R.id.tv_hello_world);
  11. btnSetText.setOnClickListener(new OnClickListener() {
  12. @Override
  13. public void onClick(View v) {
  14. mHandler.post(new Runnable() {
  15. @Override
  16. public void run() {
  17. tvHelloWorld.setText("Runnable");
  18. }
  19. });
  20. }
  21. });
  22. }
  23. }

Lint Warning原文如下:

This Handler class should be static or leaks might occur (com.example.testhandler.MainActivity.1)

Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

1. 内存泄露原因分析

由于这个Handler作为内部类声明在Activity内部,普通的内部类对象隐式地保存了一个指向外部类对象的引用,所以这个Handler对象保存了一个指向Activity对象的引用。而这个Handler对象的生命周期可能比Activity生命周期场,比如当有一个后台线程持有该Handler,别且该线程在执行一个长时间任务。所以当该Handler没有被JVM垃圾回收器回收时,它就阻止了它引用的外部类Activity对象的回收,这里就导致了内存泄露。

2. 如何解决这种内存泄露问题

在该内存泄露的Lint Warning中给出了解决该问题的方法。将Handler类声明为静态内部类,即解除内部类对象与其外部类对象之间的联系。创建一个外部类的WeakReference,并在实例化Handler对象时使用它。代码实现如下:

  1. public class MainActivity extends Activity {
  2. private TextView tvHelloWorld;
  3. private Button btnSetText;
  4. private Handler mHandler = new InternalHandler(this);
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. btnSetText = (Button) findViewById(R.id.btn_set_text);
  10. tvHelloWorld = (TextView) findViewById(R.id.tv_hello_world);
  11. btnSetText.setOnClickListener(new OnClickListener() {
  12. @Override
  13. public void onClick(View v) {
  14. mHandler.post(new Runnable() {
  15. @Override
  16. public void run() {
  17. tvHelloWorld.setText("Runnable");
  18. }
  19. });
  20. }
  21. });
  22. }
  23. private static class InternalHandler extends Handler {
  24. private WeakReference<Activity> weakRefActivity;
  25. /**
  26. * A constructor that gets a weak reference to the enclosing class. We
  27. * do this to avoid memory leaks during Java Garbage Collection.
  28. */
  29. public InternalHandler(Activity activity) {
  30. weakRefActivity = new WeakReference<Activity>(activity);
  31. }
  32. @Override
  33. public void handleMessage(Message msg) {
  34. Activity activity = weakRefActivity.get();
  35. if (activity != null) {
  36. }
  37. }
  38. }
  39. }

3. SoftReference、WeakReference和PhantomReference

SoftReference、WeakReference和PhantomReference是java.lang.ref类库中的一组类。当垃圾回收器正在考察的对象只能通过某个Reference对象才“可获得”时,这3个类为垃圾回收器提供了不同级别的间接性提示。

对象是可获得的(reachable),是指此对象可在程序中的某处找到。这意味着你在栈中有一个普通的“引用A”,而它正指向此“对象A”,也可能是“引用B”指向“对象B”,而“对象B”含有“引用C”指向“对象A”,也可能是更多的中间链接。如果一个对象是“可获得的”,垃圾回收器就不能释放它,因为它仍然为你的程序所用。如果一个对象不是“可获得的”,那么你的程序将无法使用到它,所以将其回收是安全的。

如果想继续持有某个对象的引用,想以后还能够访问到该对象,同时也想在内存消耗殆尽的时候垃圾回收器回收它,这时就应该使用Reference对象。

SoftReference、WeakReference和PhantomReference由强到弱排列,表示不同级别的“可获得性”。

SoftReference用以实现内存敏感的高速缓存。

WeakReference是为实现“规范映射”(canonicalizing mappings)而设计的,它不妨碍垃圾回收器回收映射的“键”(或“值”)。“规范映射”中对象的实例可以在程序的多处被同时使用,以节省存储空间。

PhantomReference用以调度回收前的清理工作,它比Java终止机制更加灵活。

Activity内部Handler引起内存泄露的原因分析的更多相关文章

  1. Android中使用Handler造成内存泄露的分析和解决

    什么是内存泄露?Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向 ...

  2. Android中使用Handler造成内存泄露

    1.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用 ...

  3. Android使用Handler造成内存泄露的分析及解决方法

    一.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用 ...

  4. 深入研究EF Core AddDbContext 引起的内存泄露的原因

    前两天逛园子,看到 @Jeffcky 发的这篇文章<EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下>. 一开始只是粗略的扫了一遍没仔细看,只是觉得是多次 ...

  5. Android Handler的内存泄露问题+解决方案

    谈谈handler的内存泄露问题 再来看看我们的新建Handler的代码: private Handler mHandler = new Handler() { @Override public vo ...

  6. Java内存泄露的原因

    Java内存泄露的原因 1.静态集合类像HashMap.Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象Object也不能被释放,因为他们也将一直被Vector ...

  7. 牛客网Java刷题知识点之内存溢出和内存泄漏的概念、区别、内存泄露产生原因、内存溢出产生原因、内存泄露解决方案、内存溢出解决方案

    不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑          ...

  8. 关于JVM内存溢出的原因分析及解决方案探讨

    前言:JVM中除了程序计数器,其他的区域都有可能会发生内存溢出. 0.什么是内存溢出 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出. 1. ...

  9. Handler导致内存泄露分析

    (非静态)内部类引起内存泄漏的原因         内部类的实现其实是通过编译器的语法糖(Syntactic sugar)实现的,通过生成相应的子类即以OutClassName$InteriorCla ...

随机推荐

  1. ReactNative for Android入坑(一)

    最近找工作发现有些公司要求会ReactNative,决定入坑. 搭建环境:官网详细的教程附链接. 坑一:FQ,建议整个搭建过程中FQ.(FQ链接,注册有200M试用流量,环境搭建够了)第一步:安装Ch ...

  2. 用core dump来调试程序段错误

    有的程序可以通过编译, 但在运行时会出现Segment fault(段错误). 这通常都是指针错误引起的.但这不像编译错误一样会提示到文件->行, 而是没有任何信息, 使得我们的调试变得困难起来 ...

  3. 4位bcd数转换为2进制数

    DATA   SEGMENTBUF    DW  1234HBUF1   DW  ?    ;2进制数放到buf1内存单元DATA   ENDSCODE   SEGMENTASSUME CS:CODE ...

  4. Linux硬盘命名和安装分区

    硬盘命名: 硬盘命名基于文件,一般有如下文件方式: /dev/hda1 /dev/sdb3 具体含义如下: /dev:是所有设备文件存放的目录. hd和sd:他们是区别的前两个字母,代表该分区所在的设 ...

  5. Java JNDI Datasource HOW-TO Problem

    在开发JAVA的时候发生了点问题,解决方案记录一下,在这里http://tomcat.apache.org/tomcat-7.0-doc/jndi-datasource-examples-howto. ...

  6. 解决spring mvc 上传报错,Field [] isn't an enum value,Failed to convert value of type 'java.lang.String[]' to required type '

    没有选择附件,但是点击上传按钮的时候会报错. 之前不选择文件,直接上传空文件是可以的,后来不知道改了什么就不行了. 错误信息: -- :: [http--] TRACE org.springframe ...

  7. 理解angularJS中作用域$scope

    angularJS中作用域是什么 作用域(scope)是构成angularJS应用的核心基础,在整个框架中都被广泛使用,因此了解它如何工作是非常重要的 应用的作用域是和应用的数据模型相关联的,同时作用 ...

  8. Renting Boats

    Description 长江游艇俱乐部在长江上设置了n 个游艇出租站1,2,…,n.游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇.游艇出租站i 到游艇出租站j 之间的租金为r( ...

  9. Pycharm常用快捷键(后期慢慢补充)

    用到一个,就补充一个,慢慢来,找到自己常用的快捷键. CTRL /: 注释.取消注释行 CTRL Q: 在参数列表位置,显示可以输入的所有参数. #查看参数的详细信息

  10. IOS--UIDatePicker 时间选择器 的使用方法详细

    IOS--UIDatePicker 时间选择器 的使用方法详细 // 主要有下面四种类型:   // 日期显示. // 日期和时间显示. // 时间显示. // 倒计时选择       // UIDa ...