zoukankan      html  css  js  c++  java
  • Android studio 分析内存泄漏

    以前用eclipse的时候,我们采用的是DDMS和MAT,不仅使用步骤复杂繁琐,而且要手动排查内存泄漏的位置,操作起来比较麻烦。后来随着Android studio的潮流,我也抛弃了eclipse加入了AS。

    Android Studio也开始支持自动进行内存泄漏检查,并且操作起来也比较方便。

    我们大家都知道,系统是不可能将所有的内存都分配给我们的应用程序的。每个程序都会有可使用的内存上限,这被称为堆大小(Heap Size)。不同的手机,堆大小也不尽相同,随着现在硬件设备不断提高,堆大小也提升了。如果大家想要知道自己手机的堆大小是多少,可以调用如下代码:

    ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);  
    int heapSize = manager.getMemoryClass();  

    结果是以MB为单位进行返回的,我们在开发应用程序时所使用的内存不能超出这个限制,否则就会出现OutOfMemoryError。因此,比如说我们的程序中需要缓存一些数据,就可以根据堆大小来决定缓存数据的容量。

    下面介绍下采用Android Studio Monitor来检测内存泄漏,栗子如下:

    每次启动MainActivity中时都会调用一个线程,然后这个线程会执行runnable的run方法 由于Runnable是一个匿名内部对象 所以握有MainActivity的引用,因此 
    连续启动MainActivity 4次,最后退出。按照常理来说,MainActivity会被销毁回收,可实际可能并不是这样。

    打开Android Studio,编译代码,在模拟器或者真机上运行App,在Android Monitor下点击Monitor对应的Tab,进入如下界面

     

    在Memory一栏中,可以观察不同时间App内存的动态使用情况,点击可以手动触发GC,点击可以进入HPROF Viewer界面,查看Java的Heap,切换到Package Tree View,方便查看,点击Analyzer Task,Android Monitor就可以为我们自动分析泄漏的Activity,如下图

    Reference Tree代表指向该实例的引用,可以从这里面查看内存泄漏的原因,Shallow Size指的是该对象本身占用内存的大小,Retained Size代表该对象被释放后,垃圾回收器能回收的内存总和。

    看上图,左边是内存中的对象,右边还存在4个MainActivity实例,我们明明是全部退出的,怎么还存在,这表明出现了内存泄露。

    我们查看Reference Tree, 看到 this$0个MainActivitythis$0是表示内部类的意思,也就是一个内部类引用了MainActivity 而 this$0又被 target引用, target是一个线程。内存泄漏的原因就是MainActivity被内部类引用,而内部类又被线程使用,因此无法释放造成内存泄露。
     

    内存泄漏产生的原因在Android中大致分为以下几种:

    1.static变量引起的内存泄漏 
    因为static变量的生命周期是在类加载时开始 类卸载时结束,也就是说static变量是在程序进程死亡时才释放,如果在static变量中 引用了Activity 那么 这个Activity由于被引用,便会随static变量的生命周期一样,一直无法被释放,造成内存泄漏。

    解决办法: 
    在Activity被静态变量引用时,使用 getApplicationContext 因为Application生命周期从程序开始到结束,和static变量的一样。

    2.线程造成的内存泄漏 
    类似于上述例子中的情况,线程执行时间很长,及时Activity跳出还会执行,因为线程或者Runnable是Acticvity内部类,因此握有Activity的实例(因为创建内部类必须依靠外部类),因此造成Activity无法释放。 
    AsyncTask 有线程池,问题更严重

    解决办法: 
    1.合理安排线程执行的时间,控制线程在Activity结束前结束。 
    2.将内部类改为静态内部类,并使用弱引用WeakReference来保存Activity实例 因为弱引用 只要GC发现了 就会回收它 ,因此可尽快回收

    3.BitMap占用过多内存 
    bitmap的解析需要占用内存,但是内存只提供8M的空间给BitMap,如果图片过多,并且没有及时 recycle bitmap 那么就会造成内存溢出。

    解决办法: 
    及时recycle 压缩图片之后加载图片

    4.资源未被及时关闭造成的内存泄漏 
    比如一些Cursor 没有及时close 会保存有Activity的引用,导致内存泄漏

    解决办法: 
    在onDestory方法中及时 close即可

    5.Handler的使用造成的内存泄漏 
    由于在Handler的使用中,handler会发送message对象到 MessageQueue中 然后 Looper会轮询MessageQueue 然后取出Message执行,但是如果一个Message长时间没被取出执行,那么由于 Message中有 Handler的引用,而 Handler 一般来说也是内部类对象,Message引用 Handler ,Handler引用 Activity 这样 使得 Activity无法回收。

    解决办法: 
    依旧使用 静态内部类+弱引用的方式 可解决

    6.带参数的单例

    如果我们在在调用Singleton的getInstance()方法时传入了Activity。那么当instance没有释放时,这个Activity会一直存在。因此造成内存泄露。
    解决方法:

    可以将new Singleton(context)改为new Singleton(context.getApplicationContext())即可,这样便和传入的Activity没关系了。

  • 相关阅读:
    如何在Eclipse中彻底修改一个项目名称
    用JS在html页面实现打印功能
    关于git提交、还原使用
    maven package:Max maven Unsupported major.minor version 51.0
    Tomcat 启动报错:javax.naming.NamingException: No naming context bound to this class loader
    maven web 项目中启动报错java.lang.ClassNotFoundException: org.springframework.web.util.Log4jConfigListener
    tripwire检查文件完整性
    设置mysql表名不区分大小写
    mysql-零基础安装
    nginx-0基础安装篇
  • 原文地址:https://www.cnblogs.com/kelina2mark/p/6140658.html
Copyright © 2011-2022 走看看