Android性能优化之内存泄漏

综述

内存泄漏(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存。那么在Android中,当一个对象持有Activity的引用,如果该对象不能被系统回收,那么当这个Activity不再使用时,这个Activity也不会被系统回收,那这么以来便出现了内存泄漏的情况。在应用中内出现一次两次的内存泄漏获取不会出现什么影响,但是在应用长时间使用以后,若是存在大量的Activity无法被GC回收的话,最终会导致OOM的出现。那么我们在这就来分析一下导致内存泄漏的常见因素并且如何去检测内存泄漏。

导致内存泄漏的常见因素

情景一:静态Activity和View

静态变量Activity和View会导致内存泄漏,在下面这段代码中对Activity的Context和TextView设置为静态对象,从而产生内存泄漏。


  1. import android.content.Context; 
  2. import android.support.v7.app.AppCompatActivity; 
  3. import android.os.Bundle; 
  4. import android.widget.TextView; 
  5.   
  6. public class MainActivity extends AppCompatActivity { 
  7.   
  8.     private static Context context; 
  9.     private static TextView textView; 
  10.   
  11.     @Override 
  12.     protected void onCreate(Bundle savedInstanceState) { 
  13.         super.onCreate(savedInstanceState); 
  14.         setContentView(R.layout.activity_main); 
  15.         context = this; 
  16.         textView = new TextView(this); 
  17.     } 

情景二:Thread,匿名类,内部类

在下面这段代码中存在一个非静态的匿名类对象Thread,会隐式持有一个外部类的引用LeakActivity,从而导致内存泄漏。同理,若是这个Thread作为LeakActivity的内部类而不是匿名内部类,他同样会持有外部类的引用而导致内存泄漏。在这里只需要将为Thread匿名类定义成静态的内部类即可(静态的内部类不会持有外部类的一个隐式引用)。


  1. public class LeakActivity extends AppCompatActivity { 
  2.   
  3.     @Override 
  4.     protected void onCreate(Bundle savedInstanceState) { 
  5.         super.onCreate(savedInstanceState); 
  6.         setContentView(R.layout.activity_leak); 
  7.         leakFun(); 
  8.     } 
  9.   
  10.     private void leakFun(){ 
  11.         new Thread(new Runnable() { 
  12.             @Override 
  13.             public void run() { 
  14.                 try { 
  15.                     Thread.sleep(Integer.MAX_VALUE); 
  16.                 } catch (InterruptedException e) { 
  17.                     e.printStackTrace(); 
  18.                 } 
  19.             } 
  20.         }); 
  21.     } 
  22. }  

情景三:动画

在属性动画中有一类无限循环动画,如果在Activity中播放这类动画并且在onDestroy中去停止动画,那么这个动画将会一直播放下去,这时候Activity会被View所持有,从而导致Activity无法被释放。解决此类问题则是需要早Activity中onDestroy去去调用objectAnimator.cancel()来停止动画。


  1. public class LeakActivity extends AppCompatActivity { 
  2.   
  3.     private TextView textView; 
  4.     @Override 
  5.     protected void onCreate(Bundle savedInstanceState) { 
  6.         super.onCreate(savedInstanceState); 
  7.         setContentView(R.layout.activity_leak); 
  8.         textView = (TextView)findViewById(R.id.text_view); 
  9.         ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,"rotation",0,360); 
  10.         objectAnimator.setRepeatCount(ValueAnimator.INFINITE); 
  11.         objectAnimator.start(); 
  12.     } 
  13. }  

情景四:Handler

对于Handler的内存泄漏在(Android的消息机制——Handler的工作过程)[http://blog.csdn.net/ljd2038/article/details/50889754]这篇文章中已经详细介绍,就不在赘述。

情景五:第三方库使用不当

对于EventBus,RxJava等一些第三开源框架的使用,若是在Activity销毁之前没有进行解除订阅将会导致内存泄漏。

使用MAT检测内存泄漏

对于常见的内存泄露进行介绍完以后,在这里再看一下使用MAT(Memory Analysis Tool)来检测内存泄露。MAT的下载地址为:http://www.eclipse.org/mat/downloads.php。

下面来看一段会导致内存泄露的错误代码。


  1. public class LeakActivity extends AppCompatActivity { 
  2.   
  3.     @Override 
  4.     protected void onCreate(Bundle savedInstanceState) { 
  5.         super.onCreate(savedInstanceState); 
  6.         setContentView(R.layout.activity_leak); 
  7.         EventBus.getDefault().register(this); 
  8.     } 
  9.   
  10.     @Subscribe 
  11.     public void subscriber(String s){ 
  12.   
  13.     } 
  14. }  

在上面这段代码中有会导致内存泄漏,原因是EventBus没有解除注册。下面就以这段代码为例来看一下如何分析内存泄漏。

打开AndroidStudio中的Monitors可以看到如下界面。  

在这里可以看到在应用刚启动的时候,所占用的内存为15M,然后我们现在开始操作APP,反复进入退出LeakActicity。点击上如中的GC按钮。这时候我们在看一下内存使用情况。

在这里我们可以看到,内存一直在持续增加,已经达到33M,并且无法被GC所回收。所以我们可以判断,这时候必然出现内存泄漏的情形。那么现在再点击Dump Java Heap按钮,在captures窗口看到生成得hprof文件。但这时候所生成的hprof文件不是标准格式的,我们需要通过SDK所提供的工具hprof-conv进行转化,该工具在SDK的platform-tools目录下。执行命令如下:


  1. hprof-conv XXX.hprof converted-dump.hprof 

当然在AndroidStudio中可以省去这一步,可以直接导出标准格式的hprof文件。

这时候可以通过MAT工具来打开导出的hprof文件。打开界面如下图所示:

在MAT中我们最常用的就是Histogram和Dominator Tree,他们分别对应上图中的A和B按钮。Histogram可以看出内存中不同类型的buffer的数量和占用内存的大小,而Dominator Tree则是把内存中的对象按照从大到小的顺序进行排序,并且可以分析对象之间的引用关系。在这里再来介绍一下MAT中两个符号的含义。

  • ShallowHeap:对象自身占用的内存大小,不包括他引用的对象
  • RetainedHeap:对象自身占用的内存大小并且加上它直接或者间接引用对象的大小

Histogram

由于在Android中一般内存泄漏大多出现在Acivity中,这时候可以点击Histogram按钮,并搜索Activity。  

在这里可以看出LeakActivity存在69个对象,基本上可以断定存在内存泄漏的情形,这时候便可以通过查看GC对象的引用链来进行分析。点击鼠标右键选择Merge Shortest paths to GC Roots并选择exclude weak/soft references来排除弱引用和软引用。

在排除软引用和弱引用以后如下图所示:

在这里可以看出由于EventBus导致的LeakActivity内存泄漏。

在Histogram中还可以查看一个对象包含了那些对象的引用。例如,现在要查看LeakActivity所包含的引用,可以点击鼠标右键,选择list objects中的with incoming reference。而with outcoming reference表示选中对象持有那些对象的引用。

Dominator Tree

现在我们点击这时候可以点击Dominator Tree按钮,并搜索Activity。可以看到如下图所示:

在这里可以看到存在大量的LeakActivity。然后点击鼠标右键选择Path To GC Roots->exclude weak/soft references来排除弱引用和软引用。

之后可以看到如下结果,依然是EventBus导致的内存泄漏:

总结

内存泄漏往往被我们所忽略,但是当大量的内存泄漏以后导致OOM。它所造成的影响也是不容小觑的。当然除了上述内存泄漏的分析以为我们还可以通过LeakCanary来分析内存泄漏。对于LeakCanary的使用在这里就不在进行详细介绍。

本文作者:佚名

来源:51CTO

时间: 2024-10-11 22:33:35

Android性能优化之内存泄漏的相关文章

详解Android性能优化之内存泄漏_Android

综述 内存泄漏(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存.那么在Android中,当一个对象持有Activity的引用,如果该对象不能被系统回收,那么当这个Activity不再使用时,这个Activity也不会被系统回收,那这么以来便出现了内存泄漏的情况.在应用中内出现一次两次的内存泄漏获取不会出现什么影响,但是在应用长时间使用以后,若是存在大量的Activity无法被GC回收的话,最终会导致OOM的出现.那么我们在这就来分析一下导致内存泄漏的常见因素并且如何

详解Android性能优化之内存泄漏

综述 内存泄漏(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存.那么在Android中,当一个对象持有Activity的引用,如果该对象不能被系统回收,那么当这个Activity不再使用时,这个Activity也不会被系统回收,那这么以来便出现了内存泄漏的情况.在应用中内出现一次两次的内存泄漏获取不会出现什么影响,但是在应用长时间使用以后,若是存在大量的Activity无法被GC回收的话,最终会导致OOM的出现.那么我们在这就来分析一下导致内存泄漏的常见因素并且如何

【宝贵经验】Android性能优化之内存优化实战

1. Memory Leak 内存泄漏:对于Java来说,就是new出来的Object 放在Heap上无法被GC回收(内存中存在无法被回收的对象):内存泄漏发生时的主要表现为内存抖动,可用内存慢慢变少. 1.1 Memory Monitor AndroidStudio自带的Memory Monitor可以方便的观察堆内存的分配情况,并且可以粗略的观察有没有Memory Leak. 频繁的内存抖动,可能存在内存泄漏 A:initiate GC 手动触发GC操作; B:Dump Java Heap

android,性能优化,内存优化管理,高级缓存

http://blog.csdn.net/liao3841054/article/details/7011757 这近做的项目老是出现内存溢出,项目一大,稍不注意就会出现这样 的问题.导致第二个版本框架重写,重要的还是继承体系过深,导致垃圾回收总是回收不了,最后导致内存沾满无法释放. 内存对于手机来说是非常重要的. 下面总结了我们在注意创建对象时的规则,以及怎么更好更快的实行GC回收,和怎么构建高速的对象cace缓冲. 1 避免循环遍历的创建对象,哪怕对象很小,也是要占资源的. 2 尽量使对象符

Android性能优化典范 - 第2季

Google前几天刚发布了Android性能优化典范第2季的课程,一共20个短视频,包括的内容大致有:电量优化,网络优化,Wear上如何做优化,使用对象池来提高效率,LRU Cache,Bitmap的缩放,缓存,重用,PNG压缩,自定义View的性能,提升设置alpha之后View的渲染性能,以及Lint,StictMode等等工具的使用技巧. 下面是对这些课程的总结摘要,认知有限,理解偏差的地方请多多指教! 1)Battery Drain and Networking 对于手机程序,网络操作相

Android性能优化典范(一)

2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 1) Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计师

Android性能优化典范

2015新年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计师

Android群英传笔记——第十章:Android性能优化

Android群英传笔记--第十章:Android性能优化 随着Android应用增多,功能越来越复杂,布局也越来越丰富了,而这些也成为了阻碍一个应用流畅运行,因此,对复杂的功能进行性能优化是创造高质量应用的基础,本章节将为大家展示几种性能优化的方法,帮助开发者快速的构建运行速度快,相应速度快的应用程序 布局优化 内存优化 使用各种工具进行分析,优化 一.布局优化 系统在渲染图片的时候需要消耗大量的资源,一个好的UI,不仅要有良好的视觉体验,更应该具有良好的使用体验,因此布局优化就显得很重要了

Android性能优化典范 - 第3季

Android性能优化典范的课程最近更新到第三季了,这次一共12个短视频课程,包括的内容大致有:更高效的ArrayMap容器,使用Android系统提供的特殊容器来避免自动装箱,避免使用枚举类型,注意onLowMemory与onTrimMemory的回调,避免内存泄漏,高效的位置更新操作,重复layout操作的性能影响,以及使用Batching,Prefetching优化网络请求,压缩传输数据等等使用技巧.下面是对这些课程的总结摘要,认知有限,理解偏差的地方请多多交流指正! 1)Fun with