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去收集內存泄漏信息,早發現早解決。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容