天天看点

Android性能优化——内存泄漏优化

文章目录

    • JAVA虚拟机——运行时内存
        • 共享数据区
        • 线程私有区
    • GC垃圾回收器算法
        • 引用计数法
        • 可达性分析算法(根搜索算法)
    • 强软弱虚
    • 内存泄露分析
        • 产生的原因
        • 内存泄露常见原因
    • 内存分析工具
        • Android Profiler
        • MemoryAnalyzer

JAVA虚拟机——运行时内存

​ Java虚拟机在执行Java程序的过程中会在内存空间中分配出一块区域,用于程序的运行。虚拟机又会把这块所管理的内存划分为若干个不同的数据区域。这些区域分为两大块,一个是共享数据区,一个是线程私有。其中共享数据区包括方法区和堆,线程私有区包括程序计数器 PC,虚拟机栈和本地方法栈。

Android性能优化——内存泄漏优化

共享数据区

  1. 方法区:存储已被虚拟加载的类信息,常量,静态变量,即时编译后的代码等数据。方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

    运行时常量池是方法区的一部分,

  2. 堆:存放对象实例,虚拟机能管理的最大的一块内存 GC的主战场。可以处于物理上不连续的内存空间中,只要逻辑上连续即可。像我们的磁盘空间一样,是可以扩展的。如果没有内存完成实例分配,并且堆也无法再扩展,将会抛出OutOfMemoryError异常。

线程私有区

  1. 程序计数器

    相当于一个执行代码的指示器,用来确认下一行执行的代码的地址,每个线程都有一个

  2. 虚拟机栈

    Java方法的执行模型,用于存储局部变量表,操作数栈,动态链接,方法出口等。局部变量存放了编译器可知的各种类型,对象引用,和returnAddress类型,局部变量表所需的内存空间在编译期间完成分配。

    如果线程请求的栈深度,大于虚拟机所允许的深度,将抛出StackoverflowError异常。

    如果扩展时无法申请道足够的内存,就会抛出OutOfMemoryError异常。

  3. 本地方法栈

    执行native方法,也会抛出上述两种异常,有的虚拟机将本地方法栈与虚拟机栈合二为一。

GC垃圾回收器算法

引用计数法

引用计数法就是如果一个对象没有被任何引用指向,则可视之为垃圾。这种方法的缺点就是不能检测到环的存在。

什么是引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1.任何时刻计数器值为0的对象就是不可能再被使用的。那为什么主流的Java虚拟机里面都没有选用这种算法呢?其中最主要的原因是它很难解决对象之间相互循环引用的问题。

可达性分析算法(根搜索算法)

​ 根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

那么问题又来了,如何选取GCRoots对象呢?在Java语言中,可以作为GCRoots的对象包括下面几种:

(1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。

(2). 方法区中的类静态属性引用的对象。

(3). 方法区中常量引用的对象。

(4). 本地方法栈中JNI(Native方法)引用的对象。

Android性能优化——内存泄漏优化

GC是需要2次扫描才回收对象,所以我们可以用finalize去救活丢失引用的对象,当然,我们往往不会这么做。

例如:

static App a;
@Override
protected void() throws Throwable{
	super.finalize();
	a=this;
}
           

强软弱虚

  1. 强引用:平时我们编程的时候例如:Object object=new Object();那object就是一个强引用了。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题
  2. 软引用SoftReference:内存不足时回收,存放一些重要性不是很强又不能随便让清除的对象,比如图片切换到后台不需要马上显示了

    如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

  3. 弱引用WeakReference:第一次扫到了,就标记下来,第二次扫到直接回收

    如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

  4. 虚引用PhantomReference:幽灵 幻影引用 不对生存造成任何影响,用于跟踪GC的回收通知

    "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

内存泄露分析

产生的原因

一个长生命周期的对象持有一个短生命周期对象的引用,通俗讲就是该回收的对象,因为引用问题没有被回收,最终会产生OOM

内存泄露常见原因

  1. 静态成员、单例

    static 修饰的对象生命周期是在程序进程死亡时才释放,如果在 static 变量中误引用了其他对象,则会导致这个对象无法被释放,造成内存泄漏。

    **优化:**严格控制 static 的使用,确认是否必须。

  2. 未关闭、释放的资源

    如果文件流,数据库操作对象等资源,没有进行释放,导致泄漏。

    **优化:**及时关闭相应资源,正常这些资源用完就关闭、释放,或者在 Activity 销毁的时候进行关闭、释放。

  3. Bitmap

    Bitmap的解析需要占用内存,但是安卓系统只提供 8M 的内存给所有的 Bitmap,如果图片过多,那么就会造成内存溢出。

    **优化:**注意对 Bitmap 的 recycle,或者使用第三方库进行辅助管理,比如 Glide 和 Picasso。

  4. Thread、Handler

    线程执行时间较长,Activity 已经调用销毁的方法,但是线程或者 Runnable 是 Acticvity 内部类,持有 Activity 的引用(非静态内部类持有外部内的引用),导致 Acticity 无法释放,造成泄漏。

    Handler在使用中,发送 Message 对象到 MessageQueue中,然后 Looper 会轮询 MessageQueue,然后取出Message执行,但是如果一个 Message 长时间没被取出执行,而 Message 中有 Handler的引用,而 Handler 又会引用 Activity,导致 Activity 无法释放,造成泄漏。

    **优化:**在 Activity 销毁的时候结束线程,已经清空 Handle 中未发送的消息(类似这些的东西)。也可以将内部类改为静态内部类以及使用软引用来引用 Activity。

内存分析工具

Android Profiler

Android性能优化——内存泄漏优化

① 强制执行垃圾收集事件的按钮。

② 捕获堆转储的按钮。

③ 记录内存分配的按钮。

④ 放大时间线的按钮。

⑤ 跳转到实时内存数据的按钮。

⑥ 事件时间线显示活动状态、用户输入事件和屏幕旋转事件。

⑦ 内存使用时间表,其中包括以下内容:

每个内存类别使用多少内存的堆栈图,如左边的y轴和顶部的颜色键所示。

虚线表示已分配对象的数量,如右侧y轴所示。

MemoryAnalyzer

  1. 下载MemoryAnalyzer

    https://www.eclipse.org/mat/downloads.php

  2. 转换格式

    sdk 中有提供转换的工具 hprof-conv.exe,在 SDK/platform-tools 下

    使用命令如下

    hprof-conv -z 原文件 输出文件 
               
    **注:**命令中 -z 表示去除非应用消耗的内存。
  3. 开始使用

    Eclipse–Memory–Analyzer中文教程.pdf:https://download.csdn.net/download/u011077027/11152787

继续阅读