[译]Android内存泄漏的八种可能(上)

Android防止内存泄漏的八种方法(下)

Static Activities

在类中定义了静态Activity变量,把当前运行的Activity实例赋值于这个静态变量。
如果这个静态变量在Activity生命周期结束后没有清空,就导致内存泄漏。因为static变量是贯穿这个应用的生命周期的,所以被泄漏的Activity就会一直存在于应用的进程中,不会被垃圾回收器回收。

  1. static Activity activity;
  2.  
  3. void setStaticActivity() {
  4. activity = this;
  5. }
  6.  
  7. View saButton = findViewById(R.id.sa_button);
  8. saButton.setOnClickListener(new View.OnClickListener() {
  9. @Override public void onClick(View v) {
  10. setStaticActivity();
  11. nextActivity();
  12. }
  13. });

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.

弱引用不会阻止对象的内存释放,所以即使有弱引用的存在,该对象也可以被回收。

  1. private static WeakReference<MainActivity> activityReference;
  2.  
  3. void setStaticActivity() {
  4. activityReference = new WeakReference<MainActivity>(this);
  5. }

Static Views

类似的情况会发生在单例模式中,如果Activity经常被用到,那么在内存中保存一个实例是很实用的。正如之前所述,强制延长Activity的生命周期是相当危险而且不必要的,无论如何都不能这样做。

特殊情况:如果一个View初始化耗费大量资源,而且在一个Activity生命周期内保持不变,那可以把它变成static,加载到视图树上(View Hierachy),像这样,当Activity被销毁时,应当释放资源。(译者注:示例代码中并没有释放内存,把这个static view置null即可,但是还是不建议用这个static view的方法)

  1. static view;
  2.  
  3. void setStaticView() {
  4. view = findViewById(R.id.sv_button);
  5. }
  6.  
  7. View svButton = findViewById(R.id.sv_button);
  8. svButton.setOnClickListener(new View.OnClickListener() {
  9. @Override public void onClick(View v) {
  10. setStaticView();
  11. nextActivity();
  12. }
  13. });

由于View持有其宿主Activity的引用,导致的问题与Activity一样严重。弱引用是个有效的解决方法,然而还有另一种方法是在生命周期结束时清除引用,Activity#onDestory()方法就很适合把引用置空。

  1. private static View view;
  2.  
  3. @Override
  4. public void onDestroy() {
  5. super.onDestroy();
  6. if (view != null) {
  7. unsetStaticView();
  8. }
  9. }
  10.  
  11. void unsetStaticView() {
  12. view = null;
  13. }

Inner Classes

继续,假设Activity中有个内部类,这样做可以提高可读性和封装性。将如我们创建一个内部类,而且持有一个静态变量的引用,恭喜,内存泄漏就离你不远了(译者注:销毁的时候置空,嗯)。

  1. private static Object inner;
  2.  
  3. void createInnerClass() {
  4. class InnerClass {
  5. }
  6. inner = new InnerClass();
  7. }
  8.  
  9. View icButton = findViewById(R.id.ic_button);
  10. icButton.setOnClickListener(new View.OnClickListener() {
  11. @Override public void onClick(View v) {
  12. createInnerClass();
  13. nextActivity();
  14. }
  15. });

内部类的优势之一就是可以访问外部类,不幸的是,导致内存泄漏的原因,就是内部类持有外部类实例的强引用。

与上述两种情况相似,开发者必须注意少用非静态内部类,因为非静态内部类持有外部类的隐式引用,容易导致意料之外的泄漏。然而内部类可以访问外部类的私有变量,只要我们注意引用的生命周期,就可以避免意外的发生。

  1. private Object inner;
  2.  
  3. void createInnerClass() {
  4. class InnerClass {
  5. }
  6. inner = new InnerClass();
  7. }
 

Anonymous Classes

相似地,匿名类也维护了外部类的引用。所以内存泄漏很容易发生,当你在Activity中定义了匿名的AsyncTsk
。当异步任务在后台执行耗时任务期间,Activity不幸被销毁了(译者注:用户退出,系统回收),这个被AsyncTask持有的Activity实例就不会被垃圾回收器回收,直到异步任务结束。

  1. void startAsyncTask() {
  2. new AsyncTask<Void, Void, Void>() {
  3. @Override protected Void doInBackground(Void... params) {
  4. while(true);
  5. }
  6. }.execute();
  7. }
  8.  
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11. View aicButton = findViewById(R.id.at_button);
  12. aicButton.setOnClickListener(new View.OnClickListener() {
  13. @Override public void onClick(View v) {
  14. startAsyncTask();
  15. nextActivity();
  16. }
  17. });

Handler

同样道理,定义匿名的Runnable,用匿名类Handler执行Runnable内部类会持有外部类的隐式引用,被传递到Handler的消息队列MessageQueue中,在Message消息没有被处理之前,Activity实例不会被销毁了,于是导致内存泄漏。

  1. void createHandler() {
  2. new Handler() {
  3. @Override public void handleMessage(Message message) {
  4. super.handleMessage(message);
  5. }
  6. }.postDelayed(new Runnable() {
  7. @Override public void run() {
  8. while(true);
  9. }
  10. }, Long.MAX_VALUE >> 1);
  11. }
  12.  
  13. View hButton = findViewById(R.id.h_button);
  14. hButton.setOnClickListener(new View.OnClickListener() {
  15. @Override public void onClick(View v) {
  16. createHandler();
  17. nextActivity();
  18. }
  19. });

Threads

我们再次通过ThreadTimerTask来展现内存泄漏。

  1. void spawnThread() {
  2. new Thread() {
  3. @Override public void run() {
  4. while(true);
  5. }
  6. }.start();
  7. }
  8.  
  9. View tButton = findViewById(R.id.t_button);
  10. tButton.setOnClickListener(new View.OnClickListener() {
  11. @Override public void onClick(View v) {
  12. spawnThread();
  13. nextActivity();
  14. }
  15. });

全部都是因为匿名类导致的。匿名类是特殊的内部类——写法更为简洁。当需要一次性特殊的子类时,Java提供的语法糖能让表达式最少化。这种很赞很偷懒的写法容易导致泄漏。正如使用内部类一样,只要不跨越生命周期,内部类是完全没问题的。但是,这些类是用于产生后台线程的,这些Java线程是全局的,而且持有创建者的引用(即匿名类的引用),而匿名类又持有外部类的引用。线程是可能长时间运行的,所以一直持有Activity的引用导致当销毁时无法回收。
这次我们不能通过移除静态成员变量解决,因为线程是于应用生命周期相关的。为了避免泄漏,我们必须舍弃简洁偷懒的写法,把子类声明为静态内部类。

 
但是,如果你坚持使用匿名类,只要在生命周期结束时中断线程就可以。
  1. private Thread thread;
  2.  
  3. @Override
  4. public void onDestroy() {
  5. super.onDestroy();
  6. if (thread != null) {
  7. thread.interrupt();
  8. }
  9. }
  10.  
  11. void spawnThread() {
  12. thread = new Thread() {
  13. @Override public void run() {
  14. while (!isInterrupted()) {
  15. }
  16. }
  17. }
  18. thread.start();
  19. }

Android - 内存泄漏的情况以及解决方法的更多相关文章

  1. Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  2. Android内存泄漏总结

    内存泄漏问题老生常谈,很常见也很难根治,今天我在这里总结一下内存泄漏的原因和解决方法: 所谓内存泄漏,就是本该被回收的对象,由于某些原因不能被回收,继续占用堆内存的这种状态,导致的结果也是显而易见的, ...

  3. 【腾讯优测干货分享】Android内存泄漏的简单检查与分析方法

    本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d14047603a5bf1242ad01b 导语 内存泄漏问题大约是An ...

  4. Android内存优化10 内存泄漏常见情况1 静态泄漏

    1,内存泄漏到本质是该释放的对象被持久化的对象引用了,造成持久化的常见情况有1,静态持久化 2,线程持久化 线程持久化 因为存活的线程是有dvk虚拟久直接持有,所以存活的线程都是持久化的 内存泄漏1: ...

  5. Java虚拟机系列(三)---内存溢出情况及解决方法

    因为Java虚拟机内存有堆内存.方法区.虚拟机栈.本地方法栈和程序计数器五部分组成,其中程序计数器是唯一一块不会发生内存溢出异常的内存区,所以只有四类内存区可能发生内存溢出异常,其中虚拟机栈和本地方法 ...

  6. 【转】android 内存泄漏相关收藏博客。

    关于android内存泄漏的研究   博客建了几个月,都没有去写,一是因为当时换工作,然后又是新入职(你懂的,好好表现),比较忙:二是也因为自己没有写博客的习惯了.现在还算是比较稳定了,加上这个迭代基 ...

  7. 关于android内存泄漏的研究

    博客建了几个月,都没有去写,一是因为当时换工作,然后又是新入职(你懂的,好好表现),比较忙:二是也因为自己没有写博客的习惯了.现在还算是比较稳定了,加上这个迭代基本也快结束了,有点时间来写写博客.好了 ...

  8. Android 内存泄漏优化汇总

    android内存泄漏优化摘要 博客分类: android android内存溢出OutOfMemoryError . android移动应用程序的内存分配一般是8凯瑟琳约,不正确地假定处理内存处理非 ...

  9. android 内存泄漏分析技巧

    java虚拟机执行一般都有一个内存界限,超过这个界限,就会报outofmemory.这个时候一般都是存在内存泄漏.解决内存泄漏问题,窃以为分为两个步骤:分析应用程序是否真的有内存泄漏,找到内存泄漏的地 ...

随机推荐

  1. mysql主从同步错误,提示The server quit without updating PID file

    在安装完lnmp后,启动mysqld失败,提示 [root@centos-6 ~]# service mysqld start Starting MySQL [确定][root@centos-6 ~] ...

  2. [Objective-C语言教程]数据类型(5)

    在Objective-C编程语言中,数据类型是指用于声明不同类型的变量或函数的扩展系统. 变量的类型决定了它在存储中占用的空间大小以及如何解释存储的位模式. Objective-C中的类型可分为以下几 ...

  3. [Swift实际操作]八、实用进阶-(9)Swift中的链表LinkedList详解

    链表是一种物理存储单元上的非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的.相比于线性表的顺序结构,链表比较方便插入和删除操作.本文将讲解如何模拟一个链表. //链表的节点 ...

  4. nginx负载均衡配合keepalived服务案例实战

    本实验用4台 centos6 虚拟机,2台做负载均衡,2台做web服务器,都先装上nginx lb01:192.168.0.235  --主负载均衡器 lb02:192.168.0.236  --备负 ...

  5. Mongo限制规则

    文章翻译自来源:http://docs.mongodb.org/manual/reference/limits/#limit-bson-document-size 一.BSON 文档 1.BSON文档 ...

  6. 大象——Thinking in UML

    目录 大象--Thinking in UML 面向对象 普通民众的面向对象 大师眼中的面向对象 大象--Thinking in UML 大音希声,大象希形, 近来闲暇,随手翻起一些曾经看过的书籍,才发 ...

  7. python(unittest)报告导出(一):使用HTMLTestRunner导出

    (前提:HTMLTestRunner.py放在python安装目录的Lib文件夹下) 一般将HTMLTestRunner.py文件放入需要引用的目录下,但这个太过于局限,仅对当前项目有用,所以可以将H ...

  8. 苹果Air A1466进入系统黑屏

    现象:苹果Air A1466笔记本安装Windows 7系统后,安装官网对应型号的bootcamp后,重启机器,在Windows滚动条完成后随即进入黑屏状态,安全模式能够进入,在安全模式下卸载删除显卡 ...

  9. 1095. Maximum Swap —— Weekly Challenge

    题目限定输入是[0, 10^8],因而不用考虑负数或者越界情况,算是减小了难度. public class Solution { /** * @param num: a non-negative in ...

  10. 4:Median of Two Sorted Arrays

    here are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two ...