🔥 Hi,我是小余。本文已收录到 GitHub · Androider-Planet 中。这儿有 Android 进阶生长常识体系,重视公众号 [小余的自习室] ,在成功的路上不走失!

一.内存基础常识

1.Java内存生命周期:

  • 1.创立阶段(Created
    体系通过以下步骤来创立java目标:

    • 1.为目标分配内存空间
    • 2.结构目标
    • 3.从超类对子类顺次对static成员初始化,类的初始化在ClassLoader加载该类的时分进行
    • 4.超类成员变量按次序初始化,递归调用超类的结构函数
    • 5.子类成员变量按次序初始化,一旦子类被创立,子类结构办法就调用该目标,给目标变量赋值
  • 2.运用阶段(InUse

目标至少被一个强引证持有,除非显现的运用软引证,弱引证,虚引证

  • 3.不行见阶段(InVisible

这儿的不行见是相关于运用程序来说的,对运用程序来说不行见,可是其在内存中或许还是一向存在,最常见的状况便是办法中的临时变量,程序履行现已超出其作用域范围,可是其仍或许被虚拟机中的静态变量或许jni等强引证持有。这些强引证便是所谓的“GC Root”,这也是导致内存走漏的直接原因

  • 4.不行达阶段(UnReachable

指目标不再被任何强引证持有,GC发现该目标现已不行达

  • 5.收集阶段(Collected

GC发现该目标现已不行达而且GC现已对该目标从头分配内存做好准备。假如现已类完成了finalize()办法,则会履行。这儿要特别阐明一下:不要重载finazlie()办法!原因有两点:
1.会影响JVM的目标分配与收回速度
2.或许形成该目标的再次“复生”

  • 6.完结阶段(Finalized

目标的finalize()函数履行完成后,目标仍处于不行达状况,该目标进入完结阶段

  • 7.内存从头分配阶段(Deallocaled

GC对该目标占用的内存空间进行收回或许再分配,该目标彻底消失。

2.Java内存模型

为了更好的管理内存,JVM将内存分为几部分

java内存模型-运转时数据区.png

  • 办法区:存储类信息,常量,静态变量,一切线程同享区
  • 堆区:内存最大的区域,一切线程创立的目标都在这个区分配内存,而在虚拟机栈中分配的仅仅指向堆中目标的引证。
    GC首要是对这部分区域进行处理,也是内存走漏发生的首要区域,一切线程同享区。
  • 虚拟机栈:存储当时线程的局部变量表,操作数栈等数据,线程独有区
  • 本地办法栈:和虚拟机栈相似,仅仅这个区是用于native层
  • 程序计数器:存储当时线程履行目标办法到哪行。

3.GC收回哪些目标(所谓的“废物”)

运用下面两种办法能够判别哪些是废物:

  • 1.引证计数法

给目标添加一个引证计数,当目标被强引证顺次,则计数器+1,强引证接触,计数器-1,假如在Gc的时分,计数器为0,则阐明没有目标引证,目标是废物,能够收回。

引证计数.png
可是这种办法解决不了循环引证的状况:如A引证B,B引证A,这样A和B永久无法被收回,形成内存走漏,于是就有了下面这种办法

引证计数循环引证.png

  • 2.根查找

界说一系列的“GC Root”节点为初始节点,从这个节点开端向下查找走过的路径即为一条引证链,假如一个目标没有被任何引证链引证,那就阐明这个目标不行达,目标是废物,能够被收回

gcroot.png

4.Java虚拟机内存收回算法:

  • 1.标记-整理算法

标记 GC roots可达的目标,整理掉没有被标记的目标。不移动目标,适合存活率较高的内存块。会发生内存碎片

  • 2.标记-整理算法

先运用标记-整理算法铲除废物,然后将内存由后往前移动,铲除内存碎片,同时更新目标的指针,会有必定功用耗费

  • 3.仿制-铲除算法

将内存块分为目标区和闲暇区,目标分配在目标区,当目标区满时,铲除目标区废物,然后将未铲除的目标仿制到闲暇区,清空目标区,
再让目标区和闲暇区交换,这样就能够通过仿制的办法铲除内存碎片,比移动目标更高效,适合存活目标比较少的状况,会浪费一半的内存

  • 4.分代收回战略

本质归于前三种算法的实际运用 新生代,老时代,永久代,大部分虚拟机厂商运用这个办法进行gc

新生代:朝生夕灭,存活时刻短。eg:某一个办法的局部变量,循环内的临时变量等等。
老时代:生存时刻长,但总会死亡。eg:缓存目标,数据库衔接目标,单例目标等等。
永久代:几乎一向不灭。eg:String池中的目标,加载过的类信息。

5.Android内存收回机制

在Android的高档体系版别中,针对Heap空间有一个Generational Heap Memory的模型:

Android内存收回机制.awebp

其间将整个内存分为三个区域

  • 1、Young Generation(年青热恋区,来得快去的也快)
    由一个Eden区和两个Survivor区组成,

    • 1.程序中生成的大部分新的目标都在Eden区中,
    • 2.当Eden区满时,还存活的目标将被仿制到其间一个Survivor区,
    • 3.当此Survivor区满时,此区存活的目标又被仿制到另一个Survivor区,
    • 4.当这个Survivor区也满时,会将其间存活的目标仿制到年老代。
  • 2、Old Generation年老不动区,说我老的,都被gc了
    晚年区,年纪较大,阐明不容易被收回

  • 3、Permanent Generation
    寄存静态类和办法(在 JDK 1.8 及之后的版别,在本地内存中完成的元空间(Meta-space)现已替代了永久代)

6.GC类型:

  • kGcCauseForAlloc:分配内存不行导致的gc,这个时分会触发:stop world。

logcat日志:

zygote: Alloc concurrent copying GC freed 5988(382KB) AllocSpace objects, 381(382MB) LOS objects, 59% free, 1065KB/2MB, paused 2.809ms total 87.364ms
  • kGcCauseBackground:内存到达必定阈值就会触发后台gc,后台gc不会导致stopworld。

logcat日志:

zygote: Background concurrent copying GC freed 3246(222KB) AllocSpace objects, 19(19MB) LOS objects, 1% free, 364MB/370MB, paused 19.882ms total 60.926ms
  • kGcCauseExplicit:显现调用时System.gc()触发的gc。

logcat日志:

zygote: Explicit concurrent copying GC freed 32487(1457KB) AllocSpace objects, 6(120KB) LOS objects, 39% free, 9MB/15MB, paused 796us total 40.132ms
  • kGcCauseForNativeAlloc:native层内存分配缺乏时触发

还有一些如kGcCauseCollectorTransitionkGcCauseDisableMovingGc等等就不介绍了

通过logcat中日志也能够看到当时运用内存的一个运用状况,做出一些针对性的优化

在Dalvik虚拟机下,GC的操作都是并发的,也就意味着每次触发GC都会导致其它线程暂停作业(包含UI线程)。
而在ART办法下,GC时不像Dalvik仅有一种收回算法,ART在不同的状况下会挑选不同的收回算法
比方Alloc内存不行时会选用非并发GC,但在Alloc后,发现内存到达必定阈值时又会触发并发GC。
所以在ART办法下,并不是一切的GC都对错并发的。

7.LMK 机制

LMK 全称Low Memory Killer`,LMK是一种依据内存阈值级别触发的内存收回的机制,在体系可用内存较低时,就会挑选性杀死进程的战略。

在挑选要Kill的进程的时分,体系会依据进程的运转状况作出评估,权衡进程的“重要性“,其权衡的依据首要是四大组件

假如需求减缩内存,体系会首要消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以收回体系资源。

LMK.webp
在Android中,运用进程划分5级(摘自Google文档):Android中APP的重要性层次总共5级:

前台进程(Foreground process)

可见进程(Visible process)

服务进程(Service process)

后台进程(Background process)

空进程(Empty process)

每个层级的进程都有对应的进程优先级,每个进程的优先级不是固定的,如一个前台进程进入后台后,AMS就会建议更新进程优先级的恳求,

在5.0曾经进程优先级更新直接由AMS调用内核完成,而5.0今后体系单独发动了一个lmkd服务用于处理进程优先级的问题。

Android后台杀死系列:LowMemoryKiller原理

二.常用内存优化东西

1.LeakCanary

LeakCanary是Square公司为Android开发者供给的一款基于MAT的自动检测内存走漏的东西,说它是东西其实便是一个jar包。

一.根本运用办法

  • 1.导入依靠库
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
  • 2.咱们在app中设置创立了一个list
class MyApp:Application(){
    val viewList = mutableListOf<View>()
    override fun onCreate() {
        super.onCreate()
    }
}
fun MyApp.setView(view: View){
    viewList.add(view)
}
fun MyApp.removeView(view: View){
    viewList.remove(view)
}
  • 3.在Activity中调用:
onCreate{
    1.将view放入app的viewList中
    app?.setView(tv)
    2.调用:watcher监控tv
    AppWatcher.objectWatcher.expectWeaklyReachable(tv,"test main leak")
}
  • 4.此时运转后: 日志出现:
D/LeakCanary: Found 1 object retained, not dumping heap yet (app is visible & < 5 threshold)

LeakCanary检测到有个目标没有被收回,告诉栏会有显现,点击告诉栏logcat中会显现对应的目标信息 咱们退出该运用检查日志体系:

D/LeakCanary: Found 5 objects retained, dumping heap now (app is invisible)
    ┬───
    │ GC Root: System class
    │
    ├─ android.provider.FontsContract class
    │    Leaking: NO (MyApp↓ is not leaking and a class is never leaking)
    │    ↓ static FontsContract.sContext
    ├─ com.allinpay.testkotlin.leakcanary.MyApp instance
    │    Leaking: NO (Application is a singleton)
    │    mBase instance of android.app.ContextImpl
    │    ↓ MyApp.viewList
    │            ~~~~~~~~
    ├─ java.util.ArrayList instance
    │    Leaking: UNKNOWN
    │    Retaining 101.2 kB in 1415 objects
    │    ↓ ArrayList[0]
    │               ~~~
    ╰→ com.google.android.material.textview.MaterialTextView instance
    ​     Leaking: YES (ObjectWatcher was watching this because test main leak and View.mContext references a destroyed
    ​     activity)
    ​     Retaining 101.1 kB in 1413 objects
    ​     key = f15737ab-8866-4235-af60-7cec30a144b3
    ​     watchDurationMillis = 17861
    ​     retainedDurationMillis = 12856
    ​     View is part of a window view hierarchy
    ​     View.mAttachInfo is null (view detached)
    ​     View.mID = R.id.tv
    ​     View.mWindowAttachCount = 1
    ​     mContext instance of com.allinpay.testkotlin.MainActivity with mDestroyed = true

能够看到可疑溢出的地方:

FontsContract.sContext->MyApp.viewList->ArrayList[0]->MaterialTextView instance->activity

当然也能够直接在设备上检查:

LeakCanary.png

问题原因:MyApp.viewList持有activity中的TextView引证,导致Activity退出的时分,TextView没有退出,Activity目标无法被收回,终究引起内存溢出。

解决办法:Activity退出的时分铲除viewList中的view,如下: 咱们在Activity的onDestroy处调用如下代码: tv?.let { app.viewList.remove(it) } 退出运用后,告诉栏没有显现,logcat日志显现: LeakCanary: All retained objects have been garbage collected 阐明并没有内存溢出的状况

二:作业原理:

  • 1.运用发动的时分运用Provider对Activity生命周期进行监听
  • 2.Activity在调用onDestroy的时分回调触发LeakCanary的事件监听,运用的是一个 ReferenceQueue进行监听
  • 3.假如内存中持有了未开释的Activity实例,则会调用自界说的GC处理器的runGc办法进行收回
  • 4.通过3中收回后,还有目标未开释,则会在logcat,toast和告诉栏提示dumpHeap操作,并在dumpHeap后对dump文件进行剖析,运用的是shark开源库
  • 5.dump成功后以告诉办法显现,并挂载了一个Intent,在点击告诉的时分会履行这个挂载Intent,显现heap的状况信息

2.Android Studio Profiler

Android Profiler是as供给的一个用于扫描设备某个时刻点的内存运用状况,或许一段时刻内的内存运用状况。
新版别的asp功用和运用愈加方便。

怎么运用

  • 1.直接点击东西栏的profier按钮。

asp1.png

  • 2.在Profiler面板中挑选Memory

asp2.png

  • 3.这时分会显现当时设备的内存运用状况如下。

asp3.png

这儿来阐明下内存信息框:

- 1.Others:体系不确定归类到哪一类的内存
- 2.Code:存储代码和资源的信息,如dex字节码,通过优化或许编译后逇dex代码,.so库和字体等
- 3.Statck:原生仓库和 Java 仓库运用的内存。这通常与您的运用运转多少线程有关。
- 4.Graphics:图形缓冲区行列为向屏幕显现像素(包含 GL 外表、GL 纹路等等)所运用的内存。(请留意,这是与 CPU 同享的内存,不是 GPU 专用内存。)
- 5.Native:从 C 或 C++ 代码分配的目标的内存。
- 6.Java:从 Java 或 Kotlin 代码分配的目标的内存。
- 7.Allocated:您的运用分配的 Java/Kotlin 目标数。此数字没有计入 C 或 C++ 中分配的目标
  • 4.先点击asp中的废物桶进行一次gc,切换到Android设备点击到下一个页面假设为页面B,然后切换到asp点击那个下载按钮也便是“dump java heap”
    这个时分会生成一个heap dump文件,这个文件便是当时点设备的内存运用状况、

asp4.png
这儿咱们来看标示中的5个地方:

标示1:挑选需检查的堆

  • View all heaps:检查一切的heap状况。
  • View app heap:您的运用在其间分配内存的主堆。
  • View image heap:体系发动映像,包含发动期间预加载的类。此处的分配保证绝不会移动或消失。
  • View zygote heap:写时仿制堆,其间的运用进程是从 Android 体系中派生的。

标示2:挑选怎么排序

  • Arrange by class:依据类称号对一切分配进行分组。这是默认值。
  • Arrange by package:依据软件包称号对一切分配进行分组。
  • Arrange by callstack:将一切分配分组到其对应的调用仓库。

标示3:挑选显现哪些类

  • Show all classes:展示一切Class类(包含体系类),这是默认值。
  • Show activity/fragment Leaks:展示走漏的activity/fragment。
  • Show project class:展示项目的Class类。

标示4:这儿能够查找你要查找的类,比方你怀疑某个目标存在内存走漏,能够直接查找这个类。

标示5:点击这儿能够直接显现asp供给给咱们的内存走漏状况

标示6:这儿显现当时设备内存运用具体状况

  • Allocations:当时内存中类目标个数
  • Native Size:此目标类型运用的原生内存总量(以字节为单位)。只要在运用 Android 7.0 及更高版别时,才会看到此列
    您会在此处看到选用 Java 分配的某些目标的内存,由于 Android 对某些结构类(如 Bitmap)运用原生内存。
  • Shallow Size:此目标自身占有的内存(以字节为单位)。
  • Retained Size:此目标引证链上的一切目标的总内存运用(以字节为单位)
  • Depth:从任意 GC 根到选定实例的最短跳数

怎么检查内存走漏的引证链

点击或许走漏的类型,然后点击具体列表的“References”,并把“Show nearest GC root only”勾上。
就能够显现当时目标走漏的引证链。这儿看到是由于MyApp中的viewList目标引证了MainActivity中的context导致。
和前面LeakCanary剖析的结果共同。

asp5.png

怎么检查一段时刻内的内存运用状况:

在Memory界面单击,然后就会显现一段区间,开发者能够依据需求挑选开端和完毕时刻。

asp差异.png

不止上面这些功用,ASP还能够对办法路径上的目标创立个数和巨细进行可视化调查,那这个功用就很强壮了

asp visual.png

3.MAT

关于MAT这儿上面也说过了,这儿说两点:

  • 1.一般导出的heap文件,需求通过prof东西进行转化后才干导入到MAT中
  • 2.其间的两个heap文件比照功用确实很强壮,能够准确剖分出两个heap前后内存运用状况

关于MAT运用办法能够参考这篇文章:

比较于传统内存剖析东西。
MAT的强壮之处在于其不只能够剖析某个时刻段也能够剖析两个heap文件前后比照,从而获取两个前后业务的内存运用差异。

前面剖析过ASP彻底支撑这个功用,并进行替代,而且MAT剖析步骤杂乱,ASP能够和代码无缝衔接。你会选哪一种呢?

三.开发中常见内存走漏场景

内存走漏能够简略了解为体系无法收回无用的目标。
这儿总结下项目开发过程中经常出现几种内存走漏场景以及解决方案。

  • 1.资源未开释

比方

  • 1.BroadcastReceiver没有反注册
  • 2.Cursor没有及时封闭
  • 3.各种流没有封闭
  • 4.Bitmap没有调用recycle进行收回
  • 5.Activity退出时,没有取消RxJava和协程敞开的异步任务
  • 6.Webview

一般状况下,在运用中只需运用一次 Webview,它占用的内存就不会被开释,解决方案:咱们能够为WebView敞开一个独立的进程,运用AIDL与运用的主进程进行通信,WebView所在的进程能够依据业务的需求挑选适宜的机遇进行毁掉,到达正常开释内存的目的。

  • 2.静态变量存储大数据目标

前面剖析过静态变量是存储在办法区的, 而办法区是一个生命周期比较长,不容易被收回的区域,假如静态变量存储的数据内存占用较大,就很容易出现内存走漏并发生OOM。

  • 3.单例

单例中假如运用了Activity的context,则会形成内存走漏,解决方案:运用Application的context。
或许运用弱引证去包裹context,在运用的时分去获取,假如获取不到,阐明被收回了,回来注入一个新的Activity的context。

  • 4.非静态内部类的静态实例

这儿咱们首要来讲解下静态内部类和非静态内部类差异:

  • 1.静态内部类不持有外部类的引证

    而非静态内部类持有外部类的引证
    非静态内部类能够拜访外部类的一切特点,办法,即使是private,便是由于这个原因,
    而静态内部类只能拜访外部类的静态办法和静态特点。

    • 2.静态内部类不依靠外部类

    非静态内部类和外部类是寄生联系的,同生死。而静态内部类不依靠外部类,外部类被收回了,他自己并不会被收回,你能够了解为是一个新的类:编译后格局:外部类$内部类。
    这点从结构办法也能够看出:

    非静态内部类:Inner in = new Outer().new Inner();

    静态内部类:Inner in = new Outer.Inner();

非静态内部类需求创立一个外部目标才干创立内部,所以是共生联系。这儿共生是指外部类没了,内部类也就没了,而反过来假如内部类没了,外部类是或许还存在的。
而静态内部类并没有创立一个外部目标,所以是独立存在的一个目标,办法如内部类,其实是一个新的类。

通过上面的剖析,可知,假如对错静态的内部类的静态实例会一向持有外部类的引证,假如外部类是一个Activity或许持有一个Activity的引证,则就或许导致内存走漏,
这点咱们开发的时分需求特别留意下。

  • 5.Handler临时性内存走漏

Message宣布之后存储在MessageQueue中,在Message中存在一个target,它是Handler的一个引证,Message在Queue中存在的时刻过长,就会导致Handler无法被收回。
假如Handler对错静态的,上面咱们说过非静态内部类持有外部类的引证,也便是Activity或许Service的引证,这就导致Activity或许Service无法被收回。
解决方案:
1.对Handler运用静态类的办法,然后对Handler持有的目标运用弱引证,这样就算Handler没有被开释,也能够开释Handler持有的context目标
2.在Activity退出的时分,记住remove掉音讯行列中的音讯。

  • 6.容器中的目标没整理形成的内存走漏

在退出程序之前,将调集里的东西clear,然后置为null,再退出程序

  • 7.运用ListView时形成的内存走漏

在结构Adapter时,运用缓存的convertView。

四.内存优化怎么在实际项目中实践

1.运转时内存检测优化

运转是内存是指某个页面跳转到另外一个页面,或许在某个业务需求履行某个任务,看任务前后的内存比照,这儿咱们能够运用两种办法:
办法1:运用MAT比照两个页面时的内存数据,找出或许的走漏点
办法2:运用ASP指定内存起始和完毕,然后运用“show nearest gc root”检查内存运用状况

2.静态内存优化

这边说的静态内存指的是在伴随着App的整个生命周期一向存在的那部分内存,也便是打底的,具体获取这部分内存快照的办法是:
翻开App开端重度运用App,根本翻开每一个首要页面首要功用,然后回到主页,进开发者选项翻开”不保留后台活动”,然后将咱们的app退到后台。
最后GC,dump出内存快照

3.内存走漏监测

内存走漏检测除了是LeakCanary的根本功用以外,咱们还能够自界说处理结果:
首要,承继DisplayLeakService完成一个自界说的监控处理Service,代码如下:

public class LeakCnaryService extends DisplayLeakServcie {
    private final String TAG = “LeakCanaryService”;
    @Override
    protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
        ...
    }
}

重写 afterDefaultHanding 办法,在其间处理需求的数据,三个参数的界说如下:

  • heapDump:堆内存文件,能够拿到完整的hprof文件,以运用MAT剖析。
  • result:监控到的内存状况,如是否走漏等。
  • leakInfo:leak trace具体信息,除了内存走漏目标,还有设备信息。

然后在install时,运用自界说的LeakCanaryService即可,代码如下:

public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        mRefWatcher = LeakCanary.install(this, LeakCanaryService.calss, AndroidExcludedRefs.createAppDefaults().build());
    }
    ...
}

拿到内存信息后:咱们就能够把数据保存在本地、上传到服务器进行剖析。

五.开发中内存运用留意点

1.运用弱引证

1.某些状况下能够运用弱引证,这样能够让目标能够及时收回,防止内存走漏

2.内存复用

内存的充分利用,能够协助咱们设备大大减小OOM的发生概率。

那么怎么做到内存复用呢

  • 1.资源复用:通用的字符串、颜色界说、简略页面布局的复用
  • 2.视图复用:能够运用ViewHolder完成ConvertView复用。
  • 3.目标池:显现创立目标池,完成复用逻辑,对相同的类型数据运用同一块内存空间。
  • 4.Bitmap目标的复用:运用inBitmap属功用够奉告Bitmap解码器尝试运用现已存在的内存区域,新解码的bitmap会尝试运用之前那张bitmap在heap中占有的pixel data内存区域。

3.图片优化

图片OOM问题发生的几种状况

  • 1.一个页面加载过多的图片
  • 2.加载大图片没有进行紧缩
  • 3.Android列表加载许多的bitmap没有运用缓存

Android支撑的图片格局

  • png:无损紧缩的图片格局,支撑通明通道,占用的空间一般比较大
  • Jpeg:有损紧缩的图片格局,不支撑通明通道
  • webp:由谷歌2010年发布,支撑无损与有损,比较抱负
  • gif:支撑多帧动画,但安卓自身图片库不支撑,需求用到第三方结构

图片贮存优化的办法

1.尺度优化:通过减小宽高来完成

2.质量紧缩:改动一个像素占用的内存(优化解码率)

3.内存重用:需求用到inBitmap特点

尺度优化:首要起作用的为两个办法

intJustDecodeBounds=true(能够在不加载图片的状况下取得图片的宽高)
inSampleSize(用适宜的紧缩比)

尺度紧缩.webp质量紧缩:运用RGB-565替代ARGB-8888能够降低图片占用内存

内存重用:InBitmap,后边的图需<=第一张图的巨细。

内存重用.webp

能够结合LruCache来完成,在LruCache移除超出cache size的图片时,暂时缓存Bitamp到一个软引证调集,需求创立新的Bitamp时,
能够从这个软引证调集中找到最适合重用的Bitmap,来重用它的内存区域,需求留意,新恳求的Bitmap与旧的Bitmap必须有相同的解码格局,
而且在Android 4.4之前,只能重用相同巨细的Bitamp的内存区域,而Android 4.4之后能够重用任何bitmap的内存区域。

图片加载优化的办法

  • 1.异步优化:图片放在后台恳求(不占用主UI的资源)
  • 2.图片缓存:关于列表中的图片进行缓存(本地文件中的缓存)
  • 3.网络恳求:运用OkHttp进行图片恳求(长处许多)
  • 4.懒加载:当图片出现到可视区域再进行加载

其间图片的加载一般用多级缓存加载流程:

图片缓存.webp

  • 5.运用第三方图片加载库
    现在第三方加载库都比较成熟,能够直接作为基础库进行封装。
    现在常见图片加载库比照:

图片加载三方库比照.png

4.在App可用内存过低时主动开释内存

在App退到后台内存严重即将被Kill掉时挑选重写 onTrimMemory/onLowMemory 办法去开释掉图片缓存、静态缓存来自保。

5.item被收回不行见时开释掉对图片的引证

ListView:因此每次item被收回后再次利用都会从头绑定数据,只需在ImageView onDetachFromWindow的时分开释掉图片引证即可。
RecyclerView:由于被收回不行见时第一挑选是放进mCacheView中,这儿item被复用并不会只需bindViewHolder来从头绑定数据,只要被收回进mRecyclePool中后拿出来复用才会从头绑定数据,因此重写Recycler.Adapter中的onViewRecycled()办法来使item被收回进RecyclePool的时分去开释图片引证。

6.防止创造不必要的目标

例如,咱们能够在字符串拼接的时分运用StringBuffer,StringBuilder。

7.自界说View中的内存优化

例如,在onDraw办法里面不要履行目标的创立,一般来说,都应该在自界说View的结构器中创立目标。

8.尽量运用静态内部类的办法,能够防止一部分内存走漏的发生

好了,以上便是笔者对内存优化的一些了解,希望你从中有所收获

参考

实践App内存优化:怎么有序地做内存剖析与优化

Android功用优化之内存优化

Android内存优化,内存走漏监测与问题排查

Android 内存 – 废物收回(GC)机制

Android后台杀死系列:LowMemoryKiller原理

LeakCanary官网

referenceQueue用法

Carson带你学Android:主流开源图片加载库比照(UIL、Picasso、Glide、Fresco)