zoukankan      html  css  js  c++  java
  • 性能优化-内存泄漏

    由于Java的特有属性,其垃圾回收机制的垃圾回收的时间不确定性,造成了Android的内存泄露问题,本文主要是说明一些Android中的内存泄露问题

    内存泄漏概念

    • 在C/C++中,堆内存的开辟和销毁是通过程序员通过malloc/freenew/delete去完成的,而在Java中,程序员只用开辟内存,而不用关心内存怎么去释放,这一切都交由Java的GC去完成

    • 而内存泄漏问题,也就出在GC这里,如果一个对象已经不再被使用,这时候按理说应该回收其内存,但在这时候如果有其他使用的对象正在引用这个对象,那么GC就不能对其回收,从而继续保留在内存中,这样便产生了内存泄漏,究其本质,内存泄露的原因就是内存不在GC的掌控范围之内了

    内存分区

    内存可以人为的划分为几个区

    • 静态区:存放静态数据和常量
    • 方法区:存放代码
    • 栈区:存放局部变量和基本数据类型,不会产生碎片,效率高,内存小
    • 堆区:基本通过new关键字生成的对象都位于堆区,栈中只保留其索引位置

    lrucache算法

    按照使用频率决定回收顺序

    在查阅ListView源代码的时候,发现其开辟的内存空间在UI层不可见的时候并没有被回收,而是被复用了,也就是ConvertView

    当需要加载大量数据或者图片的时候,往往是很耗费内存的,处理不当的话很容易直接造成OOM

    那么,在内存和IO之间就需要一个平衡,也就是时间和空间的平衡性

    引用分类

    另外,为了更加利于GC回收,可以使用Java的一些特殊类进行标识:StrongReferenceSoftReferenceWeakReferencePhatomReference

    • StrongReference:强引用
      • 回收时机:从不回收
      • 使用:对象的一般保存
      • 生命周期:JVM停止的时候才会终止
    • SoftReference:软引用
      • 回收时机:内存不足时
      • 使用:SoftReference<T>结合ReferenceQueue构造,有效期短
      • 生命周期:内存不足为止
    • WeakRefence:弱引用
      • 回收时机:垃圾回收时
      • 使用:SoftReference<T>结合ReferenceQueue构造,有效期短
      • 生命周期:GC回收前
    • PhatomRefence:虚引用
      • 回收时机:垃圾回收时
      • 使用:和ReferenceQueue来跟踪对象呗垃圾回收期回收的活动
      • 生命周期:GC回收前

    一些占内存且生命周期长的对象,尽量使用软引用和弱引用

    内存回收

    垃圾清理的基本概念有

    • 第一,找到未来无法存取的数据,例如所有不受指令操控的内存
    • 第二,回收被利用过的资源

    在GC运行的时候会占用内存,一样会降低系统性能,GC回收时间过长导致卡顿

    单例模式的内存泄漏

    常见的内存泄漏之一就是单例模式的使用,当活动销毁的时候,单例模式的内存并没有被销毁,此时就存在内存泄漏了

    private static CtxUtil instance;
        private Context context;
        private CtxUtil(Context context){
            this.context = context;
        }
    
        public static CtxUtil getInstance(Context context){
            if(instance == null){
                instance = new CtxUtil(context);
            }
            return instance;
        }
    

    如果在调用的时候传递的是当前对象,那么很容易造成调用者的内存泄漏

    CtxUtil ctxUtil = CtxUtil.getInstance(this);
    

    但如果传递getApplicationContext(),那么就可以解决这个问题,简单来说就是不要然类持有另一个类的实例就可以了

    CtxUtil ctxUtil = CtxUtil.getInstance(getApplicationContext());
    

    其原理就是当前对象和application的上下文生命周期并不相同
    结论:能用Applicationcontext就用Application的,单例模式产生的Context周期与app周期相同

    Android Studio检查内存泄漏

    Android Studio中有一个Monitor工具,可以用来检测内存泄漏
    主要涉及MemoryCPUGPUNetwork的监视
    选择要监视的应用,查看其各项数据的使用情况,这里主要是Memory的监视

    • 选择Initiate GC手动释放内存,这时候应用会按照自己的需求再分配内存
      如果对app进行操作,例如屏幕旋转,那么获得会销毁重新创建,此时如果Allocated数值不断上升,那么就说明存在内存泄漏问题
    • 选择Dump Java Heap,会生成堆内存快照,可以查看堆内存的使用情况
      多个快照进行对比,可以得到哪些地方内存发生内存变化,也就可以分析出内存泄漏存在的地方
      Reference Tree中可以查看引用情况

    检查内存泄漏的一般方法

    确定是否存在内存泄露

    • Android Monitors的内存分析
      最直观的看内存增长情况,知道该动作是否发生内存泄露
      假如动作发生之前:GC完后内存1.4M; 动作发生之后:GC完后内存1.6M,那么就有理由怀疑发生了内存泄漏
    • 使用MAT内存分析工具
      MAT分析heap的总内存占用大小来初步判断是否存在泄露
      Heap视图中有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类类型的对象
      data object一行中有一列是Total Size,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏
      我们反复执行某一个操作并同时执行GC排除可以回收掉的内存,注意观察data objectTotal Size值,正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况
      反之如果代码中存在没有释放对象引用的情况,随着操作次数的增多Total Size的值会越来越大

    那么这里就已经初步判断这个操作导致了内存泄露的情况

    先找怀疑对象(哪些对象属于泄露的)

    MAT对比操作前后的hprof来定位内存泄露是泄露了什么数据对象(这样做可以排除一些对象,不用后面去查看所有被引用的对象是否是嫌疑)
    快速定位到操作前后所持有的对象哪些是增加了(GC后还是比之前多出来的对象就可能是泄露对象嫌疑犯)
    技巧:Histogram中还可以对对象进行Group,比如选择Group By Package更方便查看自己Package中的对象信息

    MAT分析hprof来定位内存泄露的原因所在(哪个对象持有了上面怀疑出来的发生泄露的对象)

    • Dump出内存泄露当时的内存镜像hprof,分析怀疑泄露的类;
    • 把上面2得出的这些嫌疑犯一个一个排查个遍。步骤:
      • 进入Histogram,过滤出某一个嫌疑对象类
      • 然后分析持有此类对象引用的外部对象(在该类上面点击右键List Objects--->with incoming references
      • 再过滤掉一些弱引用、软引用、虚引用,因为它们迟早可以被GC干掉不属于内存泄露(在类上面点击右键Merge Shortest Paths to GC Roots--->exclude all phantom/weak/soft etc.references)
      • 逐个分析每个对象的GC路径是否正常,此时就要进入代码分析此时这个对象的引用持有是否合理,这就要考经验和体力了

    没有内存泄漏的标准

    • 当app退出的时候,这个进程里面所有的对象应该就都被回收了,尤其是很容易被泄露的(ViewActivity)是否还内存当中,可以让app退出以后,查看系统该进程里面的所有的ViewActivity对象是否为0,在退出以后多GC几次
    • 工具:使用AndroidStudio -> AndroidMonitor -> System Information -> Memory Usage查看Objects里面的viewsActivity的数量是否为0
    • 这样就可以知道ViewActivity是否发生了内存泄漏
  • 相关阅读:
    python D5
    python D4
    python D3
    python D2
    python D1
    day12——闭包,装饰器,迭代器
    day11——考试python2和python3的区别
    day10——动态参数,名称空间,global,nonlocal
    day9——函数初识
    day8——文件操作
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664643.html
Copyright © 2011-2022 走看看