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连接到业务 ...
随机推荐
- 【记录】HTTP协议状态码含义
状态码200-299之间的状态码表示成功300-399之间的代码表示资源已经被移走400-499之间的代码表示客户端的请求出错500-599之间的代码表示服务器出错了
- MOD 10,11算法(GB/T 17710-1999 数据处理 校验码系统 )的 Python实现
以上是算法简要说明,以下代码为Python实现,不过注意代码中的N=15,不是16. # GB/T 17710 双模校验算法 # QQ 3257132998 def GB_Code(str): str ...
- leetcode x 的平方根 python
x 的平方根 实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例 1: 输入: ...
- [UOJ240]aliens
学习了一下凸优化DP,感觉挺有意思的 首先把所有点对称到左下角,然后以每个点为顶点画等腰直角三角形,将被覆盖的点去掉,现在所有点从左上到右下横纵坐标都是递增的,设坐标为$(x_{1\cdots M}, ...
- 周末 “CTO训练营”
今天下午去中关村参加了51cto高招 “CTO训练营” 第一期. 呃蛮有收获,聊技术发展,技术cto线路或对应发展,人事对应cto发展,投资人对应看法,51cto老总的看法. 呃,挺有意思,同样认识 ...
- SGU 404 Fortune-telling with camomile
404. Fortune-telling with camomile Time limit per test: 0.25 second(s)Memory limit: 65536 kilobytes ...
- Codeforces Round #294 (Div. 2)C - A and B and Team Training 水题
C. A and B and Team Training time limit per test 1 second memory limit per test 256 megabytes input ...
- 正确率、召回率和F值
正确率.召回率和F值是在鱼龙混杂的环境中,选出目标的重要评价指标. 不妨看看这些指标的定义先: 正确率 = 正确识别的个体总数 / 识别出的个体总数 召回率 = 正确识别的个体总数 / 测试集中存 ...
- Oracle体系结构及备份(十六)——bg-ckpt
一 什么是CKPT进程 作用: 发出信号给DBWn 更新数据文件头 更新控制文件 At specific times, all modified databasebuffers in the sys ...
- MySQL运维开发相关的所有工具
http://www.ruzuojun.com/topic/592.html Percona Toolkit 安装使用 http://cenalulu.github.io/mysql/mysql- ...
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连接到业务 ...
状态码200-299之间的状态码表示成功300-399之间的代码表示资源已经被移走400-499之间的代码表示客户端的请求出错500-599之间的代码表示服务器出错了
以上是算法简要说明,以下代码为Python实现,不过注意代码中的N=15,不是16. # GB/T 17710 双模校验算法 # QQ 3257132998 def GB_Code(str): str ...
x 的平方根 实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例 1: 输入: ...
学习了一下凸优化DP,感觉挺有意思的 首先把所有点对称到左下角,然后以每个点为顶点画等腰直角三角形,将被覆盖的点去掉,现在所有点从左上到右下横纵坐标都是递增的,设坐标为$(x_{1\cdots M}, ...
今天下午去中关村参加了51cto高招 “CTO训练营” 第一期. 呃蛮有收获,聊技术发展,技术cto线路或对应发展,人事对应cto发展,投资人对应看法,51cto老总的看法. 呃,挺有意思,同样认识 ...
404. Fortune-telling with camomile Time limit per test: 0.25 second(s)Memory limit: 65536 kilobytes ...
C. A and B and Team Training time limit per test 1 second memory limit per test 256 megabytes input ...
正确率.召回率和F值是在鱼龙混杂的环境中,选出目标的重要评价指标. 不妨看看这些指标的定义先: 正确率 = 正确识别的个体总数 / 识别出的个体总数 召回率 = 正确识别的个体总数 / 测试集中存 ...
一 什么是CKPT进程 作用: 发出信号给DBWn 更新数据文件头 更新控制文件 At specific times, all modified databasebuffers in the sys ...
http://www.ruzuojun.com/topic/592.html Percona Toolkit 安装使用 http://cenalulu.github.io/mysql/mysql- ...