Android - 内存泄漏的情况以及解决方法
[译]Android内存泄漏的八种可能(上)
Static Activities
在类中定义了静态Activity
变量,把当前运行的Activity
实例赋值于这个静态变量。
如果这个静态变量在Activity
生命周期结束后没有清空,就导致内存泄漏。因为static变量是贯穿这个应用的生命周期的,所以被泄漏的Activity
就会一直存在于应用的进程中,不会被垃圾回收器回收。
static Activity activity; void setStaticActivity() {
activity = this;
} View saButton = findViewById(R.id.sa_button);
saButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
setStaticActivity();
nextActivity();
}
});
Android提供了特殊的Set
集合https://developer.android.com/reference/java/lang/ref/package-summary.html#classes
允许开发者控制引用的“强度”。Activity
对象泄漏是由于需要被销毁时,仍然被强引用着,只要强引用存在就无法被回收。
可以用弱引用代替强引用。
https://developer.android.com/reference/java/lang/ref/WeakReference.html.
弱引用不会阻止对象的内存释放,所以即使有弱引用的存在,该对象也可以被回收。
private static WeakReference<MainActivity> activityReference; void setStaticActivity() {
activityReference = new WeakReference<MainActivity>(this);
}
Static Views
类似的情况会发生在单例模式中,如果Activity
经常被用到,那么在内存中保存一个实例是很实用的。正如之前所述,强制延长Activity
的生命周期是相当危险而且不必要的,无论如何都不能这样做。
特殊情况:如果一个View初始化耗费大量资源,而且在一个Activity
生命周期内保持不变,那可以把它变成static,加载到视图树上(View Hierachy),像这样,当Activity
被销毁时,应当释放资源。(译者注:示例代码中并没有释放内存,把这个static view置null即可,但是还是不建议用这个static view的方法)
static view; void setStaticView() {
view = findViewById(R.id.sv_button);
} View svButton = findViewById(R.id.sv_button);
svButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
setStaticView();
nextActivity();
}
});
由于View
持有其宿主Activity
的引用,导致的问题与Activity
一样严重。弱引用是个有效的解决方法,然而还有另一种方法是在生命周期结束时清除引用,Activity#onDestory()
方法就很适合把引用置空。
private static View view; @Override
public void onDestroy() {
super.onDestroy();
if (view != null) {
unsetStaticView();
}
} void unsetStaticView() {
view = null;
}
Inner Classes
继续,假设Activity
中有个内部类,这样做可以提高可读性和封装性。将如我们创建一个内部类,而且持有一个静态变量的引用,恭喜,内存泄漏就离你不远了(译者注:销毁的时候置空,嗯)。
private static Object inner; void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
} View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
createInnerClass();
nextActivity();
}
});
内部类的优势之一就是可以访问外部类,不幸的是,导致内存泄漏的原因,就是内部类持有外部类实例的强引用。
与上述两种情况相似,开发者必须注意少用非静态内部类,因为非静态内部类持有外部类的隐式引用,容易导致意料之外的泄漏。然而内部类可以访问外部类的私有变量,只要我们注意引用的生命周期,就可以避免意外的发生。
private Object inner; void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
}
Anonymous Classes
相似地,匿名类也维护了外部类的引用。所以内存泄漏很容易发生,当你在Activity
中定义了匿名的AsyncTsk
。当异步任务在后台执行耗时任务期间,Activity
不幸被销毁了(译者注:用户退出,系统回收),这个被AsyncTask
持有的Activity
实例就不会被垃圾回收器回收,直到异步任务结束。
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}.execute();
} super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View aicButton = findViewById(R.id.at_button);
aicButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
startAsyncTask();
nextActivity();
}
});
Handler
同样道理,定义匿名的Runnable
,用匿名类Handler
执行。Runnable
内部类会持有外部类的隐式引用,被传递到Handler
的消息队列MessageQueue
中,在Message
消息没有被处理之前,Activity
实例不会被销毁了,于是导致内存泄漏。
void createHandler() {
new Handler() {
@Override public void handleMessage(Message message) {
super.handleMessage(message);
}
}.postDelayed(new Runnable() {
@Override public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
} View hButton = findViewById(R.id.h_button);
hButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
createHandler();
nextActivity();
}
});
Threads
我们再次通过Thread和TimerTask来展现内存泄漏。
void spawnThread() {
new Thread() {
@Override public void run() {
while(true);
}
}.start();
} View tButton = findViewById(R.id.t_button);
tButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
spawnThread();
nextActivity();
}
});
全部都是因为匿名类导致的。匿名类是特殊的内部类——写法更为简洁。当需要一次性特殊的子类时,Java提供的语法糖能让表达式最少化。这种很赞很偷懒的写法容易导致泄漏。正如使用内部类一样,只要不跨越生命周期,内部类是完全没问题的。但是,这些类是用于产生后台线程的,这些Java线程是全局的,而且持有创建者的引用(即匿名类的引用),而匿名类又持有外部类的引用。线程是可能长时间运行的,所以一直持有Activity
的引用导致当销毁时无法回收。
这次我们不能通过移除静态成员变量解决,因为线程是于应用生命周期相关的。为了避免泄漏,我们必须舍弃简洁偷懒的写法,把子类声明为静态内部类。
private Thread thread; @Override
public void onDestroy() {
super.onDestroy();
if (thread != null) {
thread.interrupt();
}
} void spawnThread() {
thread = new Thread() {
@Override public void run() {
while (!isInterrupted()) {
}
}
}
thread.start();
}
Android - 内存泄漏的情况以及解决方法的更多相关文章
- Android 内存泄漏分析与解决方法
在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...
- Android内存泄漏总结
内存泄漏问题老生常谈,很常见也很难根治,今天我在这里总结一下内存泄漏的原因和解决方法: 所谓内存泄漏,就是本该被回收的对象,由于某些原因不能被回收,继续占用堆内存的这种状态,导致的结果也是显而易见的, ...
- 【腾讯优测干货分享】Android内存泄漏的简单检查与分析方法
本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d14047603a5bf1242ad01b 导语 内存泄漏问题大约是An ...
- Android内存优化10 内存泄漏常见情况1 静态泄漏
1,内存泄漏到本质是该释放的对象被持久化的对象引用了,造成持久化的常见情况有1,静态持久化 2,线程持久化 线程持久化 因为存活的线程是有dvk虚拟久直接持有,所以存活的线程都是持久化的 内存泄漏1: ...
- Java虚拟机系列(三)---内存溢出情况及解决方法
因为Java虚拟机内存有堆内存.方法区.虚拟机栈.本地方法栈和程序计数器五部分组成,其中程序计数器是唯一一块不会发生内存溢出异常的内存区,所以只有四类内存区可能发生内存溢出异常,其中虚拟机栈和本地方法 ...
- 【转】android 内存泄漏相关收藏博客。
关于android内存泄漏的研究 博客建了几个月,都没有去写,一是因为当时换工作,然后又是新入职(你懂的,好好表现),比较忙:二是也因为自己没有写博客的习惯了.现在还算是比较稳定了,加上这个迭代基 ...
- 关于android内存泄漏的研究
博客建了几个月,都没有去写,一是因为当时换工作,然后又是新入职(你懂的,好好表现),比较忙:二是也因为自己没有写博客的习惯了.现在还算是比较稳定了,加上这个迭代基本也快结束了,有点时间来写写博客.好了 ...
- Android 内存泄漏优化汇总
android内存泄漏优化摘要 博客分类: android android内存溢出OutOfMemoryError . android移动应用程序的内存分配一般是8凯瑟琳约,不正确地假定处理内存处理非 ...
- android 内存泄漏分析技巧
java虚拟机执行一般都有一个内存界限,超过这个界限,就会报outofmemory.这个时候一般都是存在内存泄漏.解决内存泄漏问题,窃以为分为两个步骤:分析应用程序是否真的有内存泄漏,找到内存泄漏的地 ...
随机推荐
- mongodb获取具体某一天的查询语句
比如我要查询截止日期为2010-12-21这一天的数据记录.方法一. db.集合名.find({con_date:new Date("2010/12/21")})1方法二. db. ...
- 使用 webpack 搭建 React 项目
简评:相信很多开发者在入门 react 的时候都是使用 create-react-app 或 react-slingshot 这些脚手架来快速创建应用,当有特殊需求,需要修改 eject 出来的 we ...
- #PHP 数组添加元素、统计数组相同元素个数、改变数组key值~_~
一.数组添加元素 1.定义和用法: array_push() 函数向第一个参数的数组尾部添加一个或多个元素(入栈),然后返回新数组的长度. 2.语法: array_push(array,value1, ...
- 洛谷 P3285 / loj 2212 [SCOI2014] 方伯伯的 OJ 题解【平衡树】【线段树】
平衡树分裂钛好玩辣! 题目描述 方伯伯正在做他的 OJ.现在他在处理 OJ 上的用户排名问题. OJ 上注册了 \(n\) 个用户,编号为 \(1\sim n\),一开始他们按照编号排名.方伯伯会按照 ...
- Watchbog挖矿病毒程序排查过程
第1章 情况 1)服务器收到cpu报警,cpu被占用达到100%,登录服务器查看,发现cpu被一个watchbog的进程占满了,如下图所示: 2)并且无论如何都杀不掉,用kill杀掉后,其还是会隔一会 ...
- 红米手机3S 3X简单卡刷开发版获得ROOT权限的方法
小米的机器不同手机型号一般小米论坛都提供两个不同的系统,即分别是稳定版和开发版,稳定版没有提供root权限管理,开发版中就支持了root权限,很多情况下我们需要使用的一些功能强大的APP,都需要在ro ...
- 整理的最全 python常见面试题
整理的最全 python常见面试题(基本必考)① ②③④⑤⑥⑦⑧⑨⑩ 1.大数据的文件读取: ① 利用生成器generator: ②迭代器进行迭代遍历:for line in file; 2.迭代 ...
- excel表计算和计算器计算结果不一致
excel表计算和计算器计算结果不一致 : 建议安装完excel进行精度设置:
- 1.python学习计划
1.python学习 第一次使用博客园作为学习记录日志,希望能在这里记录自己的学习点滴. 慢慢去挖掘它的强大功能吧
- 算法导论学习笔记1---排序算法(平台:gcc 4.6.7)
平台:Ubuntu 12.04/gcc 4.6.7 插入排序 #include<vector> #include <algorithm> #include<iostrea ...