Handler Thread 内部类引起内存泄露分析
非静态内部类引起内存泄漏的原因
Handler引起内存泄漏案例分析
例如,当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用。而Handler通常会伴随着一个耗时的后台线程一起出现,这个后台线程在任务执行完毕之后,通过消息机制通知Handler,然后Handler把图片更新到界面。
然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收,直到网络请求结束。
另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。
Handler完整方案示例
首先:将内部类声明为静态内部类(通用方法)
在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。如果你想使用外部类的话,可以通过软引用或弱引用的方式保存外部类的引用。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)。
对于Handler,还可以通过程序逻辑来进行保护
1、在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2、如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
public class MainActivity extends Activity {
private Handler mHandler = new MyHandler(this);
public TextView textView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("包青天");
setContentView(textView);
mHandler.sendMessageDelayed(Message.obtain(), 2000);
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity) {
mWeakReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mWeakReference.get();
if (activity != null) activity.textView.setText("静态内部类的Handler");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mHandler != null) mHandler.removeCallbacksAndMessages(null);
}
}
Thread引起内存泄漏案例分析
例如在一个Activity启动一个Thread执行一个任务,因为Thread是内部类持有了Activity的引用,当Activity销毁的时候如果Thread的任务没有执行完成,造成Activity的引用不能被释放从而引起内存泄漏。
这种情况下可以通过声明一个静态内部类来解决问题,从反编译中可以看出,声明为static的内部类不会持有外部类的引用。此时,如果你想在静态内部类中使用外部类的话,可以通过软引用的方式保存外部类的引用。
在Activity里声明了一个匿名内部类,如果Activity在销毁之前,线程的任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。
/**
* 测试非静态内部类导致内存泄漏的问题
*/
public class MemoryLeaksActivity extends Activity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
setContentView(textView);
String startTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
textView.setText("开始休息 " + startTime);
new Thread(() -> {
SystemClock.sleep(1000 * 5);
String endTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
Log.i("bqt", "【结束休息】" + endTime);//即使Activity【onDestroy被回调了】,这条日志仍会打出来
//runOnUiThread(() -> textView.append("\n结束休息 " + endTime));
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("bqt", "【onDestroy被回调了】");
}
}
Thread解决方案示例
解决办法就是使用静态内部类,如下:
/**
* 测试使用静态内部类避免导致内存泄漏
*/
public class MemoryLeaksActivity extends Activity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
setContentView(textView);
String startTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
textView.setText("开始休息 " + startTime);
new MyThread(this).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("bqt", "【onDestroy被回调了】");
}
private static class MyThread extends Thread {
SoftReference<Activity> context;
MyThread(Activity activity) {
context = new SoftReference<>(activity);
}
@Override
public void run() {
SystemClock.sleep(1000 * 15);
String endTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
Log.i("bqt", "【结束休息】" + endTime);//即使Activity【onDestroy被回调了】,这条日志仍会打出来
if (context.get() != null) {
context.get().runOnUiThread(() -> Toast.makeText(context.get(), "结束休息", Toast.LENGTH_SHORT).show());
}
}
}
}
2017-8-24
Handler Thread 内部类引起内存泄露分析的更多相关文章
- Android Studio 使用Memory Monitor进行内存泄露分析
在使用Android Studio进行内存泄露分析之前,我们先回顾一下Java相关的内存管理机制,然后再讲述一下内存分析工具如何使用. 一.Java内存管理机制 1. Java内存分配策略 Java ...
- 关于内存泄露分析插件 MAT 的用法
关于内存泄露分析插件 MAT 的用法,建议大家有时间看一下,下面的文章 http://www.blogjava.net/rosen/archive/2010/05/21/321575.html htt ...
- 学会用Clang来进行内存泄露分析
最近项目出现了内存泄露的问题,对于PC x86平台来说,一点点的内存泄露往往不会出错,很难进行debug调试.这个时候我们可以用到苹果给我们带来的神器--Clang编译器来进行内存泄露分析检测,开关打 ...
- Handler导致内存泄露分析
(非静态)内部类引起内存泄漏的原因 内部类的实现其实是通过编译器的语法糖(Syntactic sugar)实现的,通过生成相应的子类即以OutClassName$InteriorCla ...
- Android handler 内存泄露分析及解决方法
1. 什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引 ...
- JVM内存管理概述与android内存泄露分析
一.内存划分 将内存划分为六大部分,分别是PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量池以及本地方法栈. 1.PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的 ...
- 【转】.. Android应用内存泄露分析、改善经验总结
原文网址:http://wetest.qq.com/lab/view/107.html?from=ads_test2_qqtips&sessionUserType=BFT.PARAMS.194 ...
- 记一次Java的内存泄露分析
当前环境 jdk == 1.8 httpasyncclient == 4.1.3 代码地址 git 地址:https://github.com/jasonGeng88/java-network-pro ...
- JVM堆内存泄露分析
一.背景 公司有一个中间的系统A可以对接多个后端业务系统B,一个业务系统以一个Namespace代表, Namespace中包含多个FrameChannel(用holder保存),表示A连接到业务 ...
随机推荐
- linux通过c++实现线程池类
目录 线程池的实现 线程池已基于C++11重写 : 基于C++11实现线程池的工作原理 前言 线程池的概念 使用原因及适用场合 线程池的实现原理 程序测试 线程池的实现 线程池已基于C++11重写 : ...
- 大数据技术之_13_Azkaban学习_Azkaban(阿兹卡班)介绍 + Azkaban 安装部署 + Azkaban 实战
一 概述1.1 为什么需要工作流调度系统1.2 常见工作流调度系统1.3 各种调度工具特性对比1.4 Azkaban 与 Oozie 对比二 Azkaban(阿兹卡班) 介绍三 Azkaban 安装部 ...
- Revit二次开发示例:AutoStamp
该示例中,在Revit启动时添加打印事件,在打印时向模型添加水印,打印完成后删除该水印. #region Namespaces using System; using System.Collect ...
- django创建model
1.model实例 #!/usr/bin/python # coding:utf-8 from __future__ import unicode_literals from django.db im ...
- 浏览器在线查看pdf文件 pdf.js的使用教程
谷歌浏览器可以直接在线查看pdf,而IE内核浏览器无法在线查看,默认是下载. 这里用到的是pdf.js,不仅支持IE内核浏览器,而且兼容手机查看pdf 官网地址:http://mozilla.gith ...
- 【JavaScript代码实现四】获取和设置 cookie
// 创建cookie function setCookie(name, value, expires, path, domain, secure) { var cookieText = encode ...
- python开发_tkinter_自己做的猜数字小程序
读到这篇文章[python 3.3下结合tkinter做的猜数字程序]的时候,就复制了代码,在自己机器上面跑了一下 源程序存在一个缺陷: 即当用户答对了以后,用户再点击'猜'按钮,最上面的提示标签还会 ...
- python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐(二)
在上一篇blog:python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐 中介绍了python中的tkinter的一些东西,你可能对tkinter有一定的了解了.这篇b ...
- SGU 403 Scientific Problem
403. Scientific Problem Time limit per test: 0.25 second(s)Memory limit: 65536 kilobytes input: stan ...
- Uva 12889 One-Two-Three
Your little brother has just learnt to write one, two and three, in English. He has written a lot ...
public class MainActivity extends Activity {
private Handler mHandler = new MyHandler(this);
public TextView textView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("包青天");
setContentView(textView);
mHandler.sendMessageDelayed(Message.obtain(), 2000);
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity) {
mWeakReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mWeakReference.get();
if (activity != null) activity.textView.setText("静态内部类的Handler");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mHandler != null) mHandler.removeCallbacksAndMessages(null);
}
}
/**
* 测试非静态内部类导致内存泄漏的问题
*/
public class MemoryLeaksActivity extends Activity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
setContentView(textView);
String startTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
textView.setText("开始休息 " + startTime);
new Thread(() -> {
SystemClock.sleep(1000 * 5);
String endTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
Log.i("bqt", "【结束休息】" + endTime);//即使Activity【onDestroy被回调了】,这条日志仍会打出来
//runOnUiThread(() -> textView.append("\n结束休息 " + endTime));
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("bqt", "【onDestroy被回调了】");
}
}
Thread解决方案示例
/**
* 测试使用静态内部类避免导致内存泄漏
*/
public class MemoryLeaksActivity extends Activity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
setContentView(textView);
String startTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
textView.setText("开始休息 " + startTime);
new MyThread(this).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("bqt", "【onDestroy被回调了】");
}
private static class MyThread extends Thread {
SoftReference<Activity> context;
MyThread(Activity activity) {
context = new SoftReference<>(activity);
}
@Override
public void run() {
SystemClock.sleep(1000 * 15);
String endTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
Log.i("bqt", "【结束休息】" + endTime);//即使Activity【onDestroy被回调了】,这条日志仍会打出来
if (context.get() != null) {
context.get().runOnUiThread(() -> Toast.makeText(context.get(), "结束休息", Toast.LENGTH_SHORT).show());
}
}
}
}
在使用Android Studio进行内存泄露分析之前,我们先回顾一下Java相关的内存管理机制,然后再讲述一下内存分析工具如何使用. 一.Java内存管理机制 1. Java内存分配策略 Java ...
关于内存泄露分析插件 MAT 的用法,建议大家有时间看一下,下面的文章 http://www.blogjava.net/rosen/archive/2010/05/21/321575.html htt ...
最近项目出现了内存泄露的问题,对于PC x86平台来说,一点点的内存泄露往往不会出错,很难进行debug调试.这个时候我们可以用到苹果给我们带来的神器--Clang编译器来进行内存泄露分析检测,开关打 ...
(非静态)内部类引起内存泄漏的原因 内部类的实现其实是通过编译器的语法糖(Syntactic sugar)实现的,通过生成相应的子类即以OutClassName$InteriorCla ...
1. 什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引 ...
一.内存划分 将内存划分为六大部分,分别是PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量池以及本地方法栈. 1.PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的 ...
原文网址:http://wetest.qq.com/lab/view/107.html?from=ads_test2_qqtips&sessionUserType=BFT.PARAMS.194 ...
当前环境 jdk == 1.8 httpasyncclient == 4.1.3 代码地址 git 地址:https://github.com/jasonGeng88/java-network-pro ...
一.背景 公司有一个中间的系统A可以对接多个后端业务系统B,一个业务系统以一个Namespace代表, Namespace中包含多个FrameChannel(用holder保存),表示A连接到业务 ...
目录 线程池的实现 线程池已基于C++11重写 : 基于C++11实现线程池的工作原理 前言 线程池的概念 使用原因及适用场合 线程池的实现原理 程序测试 线程池的实现 线程池已基于C++11重写 : ...
一 概述1.1 为什么需要工作流调度系统1.2 常见工作流调度系统1.3 各种调度工具特性对比1.4 Azkaban 与 Oozie 对比二 Azkaban(阿兹卡班) 介绍三 Azkaban 安装部 ...
该示例中,在Revit启动时添加打印事件,在打印时向模型添加水印,打印完成后删除该水印. #region Namespaces using System; using System.Collect ...
1.model实例 #!/usr/bin/python # coding:utf-8 from __future__ import unicode_literals from django.db im ...
谷歌浏览器可以直接在线查看pdf,而IE内核浏览器无法在线查看,默认是下载. 这里用到的是pdf.js,不仅支持IE内核浏览器,而且兼容手机查看pdf 官网地址:http://mozilla.gith ...
// 创建cookie function setCookie(name, value, expires, path, domain, secure) { var cookieText = encode ...
读到这篇文章[python 3.3下结合tkinter做的猜数字程序]的时候,就复制了代码,在自己机器上面跑了一下 源程序存在一个缺陷: 即当用户答对了以后,用户再点击'猜'按钮,最上面的提示标签还会 ...
在上一篇blog:python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐 中介绍了python中的tkinter的一些东西,你可能对tkinter有一定的了解了.这篇b ...
403. Scientific Problem Time limit per test: 0.25 second(s)Memory limit: 65536 kilobytes input: stan ...
Your little brother has just learnt to write one, two and three, in English. He has written a lot ...