Adnroid性能优化之内存优化

前言

 开发程序过程中常常涉及到内存的申请以及回收过程,由于表现形式不明显而且Java有自动垃圾回收机制,普遍情况下不会过度关注内存,容易疏漏导致抛出异常。同时OOM等内存问题所抛出的堆栈信息有可能不是问题的直接原因,因此还存在排查难等问题。


1 分类

  • 内存抖动:短时间内创建大量新生代对象导致GC频繁,通过观察Profiler Memory发现内存呈锯齿状。
  • 内存泄漏:已分配内存由于某种原因未能被回收,导致可用内存逐渐减少。例如,界面被销毁,但是Activity还被持有引用,导致Activity无法被回收。
  • 内存溢出:无法申请到所需的内存空间时。例如,加载图片过大导致发生程序异常,提示OutOfMemory。

2 工具使用

  • Profiler
  • Memory Analyzer(和Proiler差不多,不使用)
  • LeakCanary

3 内存抖动

 首先使用Profiler进行排查,然后再根据记录抖动位置的代码进行排查。以下面这个方法为例:

/**
     *  排序后打印二维数组,一行行打印
     */
    fun imPrettySureSortingIsFree() {
        val dimension = 300
        val lotsOfInts =
            Array(dimension) { IntArray(dimension) }
        val randomGenerator = Random()
        for (i in lotsOfInts.indices) {
            for (j in lotsOfInts[i].indices) {
                lotsOfInts[i][j] = randomGenerator.nextInt()
            }
        }
        for (i in lotsOfInts.indices) {
            var rowAsStr = ""
            //排序
            val sorted = getSorted(lotsOfInts[i])
            //拼接打印
            for (j in lotsOfInts[i].indices) {
                rowAsStr += sorted[j]
                if (j < lotsOfInts[i].size - 1) {
                    rowAsStr += ", "
                }
            }
        }
    }

    fun getSorted(input: IntArray): IntArray {
        val clone = input.clone()
        Arrays.sort(clone)
        return clone
    }

1.运行后观察Profiler发现内存GC频繁,我们可以点击record记录下内存的变化,查看这段时间的内存分配情况。


2.前面说过造成内存抖动的原因,因此可以根据内存分配的数量进行分析。点击Allocations进行排序之后,发现char[]分配的对象是最多的,因此char[]成为了首先怀疑的对象。


3.点击列表的实例查看char[]分配回调栈,通过对比发现内容都一致,因此可以基本断定是该实例导致内存抖动。

  1. 最后发现可能是MemoryPerformanceActivity中Oncreate的handleMessage或者imPrettySureSortingIsFree方法引起的抖动,我们就可以点击右键选择Jump to spurce跳转,并解决问题。

4 内存泄漏

 导致内存泄漏的原因有很多,例如Cursor、IO操作未关闭,Bitmap、Context未回收等等。我们可以通过Profler Memory堆转储功能进行内存泄漏分析。以下面方法为例:

class MemoryPerformanceActivity : AppCompatActivity(), CallBack {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_memory_performance)
        val imageView: ImageView = findViewById(R.id.imageView)
        val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
        imageView.setImageBitmap(bitmap)
        CallBackManager.addCallBack(this)
    }

    override fun mack() {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}
object CallBackManager {
    private var mCallBacks = arrayListOf<CallBack>()
    fun addCallBack(callBack: CallBack) {
        mCallBacks.add(callBack)
    }
    fun removeCallBack(callBack: CallBack) {
        mCallBacks.remove(callBack)
    }
}

1.来回跳转数次到MemoryPerformanceActivity,通过Profiler观察内存的变化,主要是查看Activity结束时Total是否有减少,我们可以如果占用内存越来越高,则可能发生内存泄露。


2.猜测可能发生内存泄漏之后,我们可点击强制GC去除引用,再点击Dump java heap,即可得到hprof文件进行分析。


3.我们可以点击Arrange by packge按包名进行排序或者点击Filter搜索myApplication。通过观察可以发现强制GC之后MemoryPerformanceActivity仍然存在6个对象。我们想要看到的是1个对象,因此这并不是我们预期想要看到的。


4.点击MemoryPerformanceActivity查看实例,可以看到每个Aactivity的实际内存分配为94000左右,引用深度Depth为3,再通过References查看引用,可以看出来是mCallBacks持有了6个Activity的引用,其Retained Size的大小为570803。最后看到shadow$_klass_in_CallBackManager,发现是CallBackManager引用了mCallBacks,点击右键选择Jump to source跳转到CallBackManager进行问题分析。


5.通过分析我们发现CallBackManager 是个静态类,持有的对象与App生命周期一样长,因此需要手动将CallBack移除。

  override fun onDestroy() {
        super.onDestroy()
        CallBackManager.removeCallBack(this)
    }

5 内存溢出

 当申请不到需要的内存时就会发生内存溢出(OOM),在开发过程中我们常见的OOM就有Bitmap加载不合理图片造成的。我们可以在线下通过ARTHoot去监测出不合理的图片,在线下就将问题暴露出来。
 从错误堆栈信息不一定能看出内存溢出的准确问题点,因为内存溢出可能只是表象,造成内存溢出的原因有可能是因为内存泄漏,导致可用内存越来越少,最后分配不到我们所需的空间,导致内存溢出。


6 LeakCanary

leakcanary是由square开源的框架,可以帮助我们快速发现内存泄漏,减少OOM。集成很简单,只需要下面这一步,原理就是利用了Provider的Oncreate比Application的生命周期还要早,内部实现了Provider并进行框架初始化。

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
}

集成之后Leakcanary就会自动帮我们收集内存泄漏信息。

总结

  • MAT和Profiler使用上差不多,因此建议使用Profiler即可,发现问题可直接跳转到相应的代码位置,提高排查效率。
  • 在开发初期集成LeakCanary去收集内存泄漏信息,早发现早解决。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容