前言
?開發(fā)程序過程中常常涉及到內(nèi)存的申請以及回收過程,由于表現(xiàn)形式不明顯而且Java有自動垃圾回收機制,普遍情況下不會過度關(guān)注內(nèi)存,容易疏漏導(dǎo)致拋出異常。同時OOM等內(nèi)存問題所拋出的堆棧信息有可能不是問題的直接原因,因此還存在排查難等問題。
1 分類
- 內(nèi)存抖動:短時間內(nèi)創(chuàng)建大量新生代對象導(dǎo)致GC頻繁,通過觀察Profiler Memory發(fā)現(xiàn)內(nèi)存呈鋸齒狀。
- 內(nèi)存泄漏:已分配內(nèi)存由于某種原因未能被回收,導(dǎo)致可用內(nèi)存逐漸減少。例如,界面被銷毀,但是Activity還被持有引用,導(dǎo)致Activity無法被回收。
- 內(nèi)存溢出:無法申請到所需的內(nèi)存空間時。例如,加載圖片過大導(dǎo)致發(fā)生程序異常,提示OutOfMemory。
2 工具使用
- Profiler
- Memory Analyzer(和Proiler差不多,不使用)
- LeakCanary
3 內(nèi)存抖動
?首先使用Profiler進行排查,然后再根據(jù)記錄抖動位置的代碼進行排查。以下面這個方法為例:
/**
* 排序后打印二維數(shù)組,一行行打印
*/
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發(fā)現(xiàn)內(nèi)存GC頻繁,我們可以點擊record記錄下內(nèi)存的變化,查看這段時間的內(nèi)存分配情況。

2.前面說過造成內(nèi)存抖動的原因,因此可以根據(jù)內(nèi)存分配的數(shù)量進行分析。點擊
Allocations進行排序之后,發(fā)現(xiàn)char[]分配的對象是最多的,因此char[]成為了首先懷疑的對象。

3.點擊列表的實例查看char[]分配回調(diào)棧,通過對比發(fā)現(xiàn)內(nèi)容都一致,因此可以基本斷定是該實例導(dǎo)致內(nèi)存抖動。

- 最后發(fā)現(xiàn)可能是MemoryPerformanceActivity中Oncreate的handleMessage或者imPrettySureSortingIsFree方法引起的抖動,我們就可以點擊右鍵選擇Jump to spurce跳轉(zhuǎn),并解決問題。

4 內(nèi)存泄漏
?導(dǎo)致內(nèi)存泄漏的原因有很多,例如Cursor、IO操作未關(guān)閉,Bitmap、Context未回收等等。我們可以通過Profler Memory堆轉(zhuǎn)儲功能進行內(nèi)存泄漏分析。以下面方法為例:
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.來回跳轉(zhuǎn)數(shù)次到MemoryPerformanceActivity,通過Profiler觀察內(nèi)存的變化,主要是查看Activity結(jié)束時Total是否有減少,我們可以如果占用內(nèi)存越來越高,則可能發(fā)生內(nèi)存泄露。

2.猜測可能發(fā)生內(nèi)存泄漏之后,我們可點擊
強制GC去除引用,再點擊Dump java heap,即可得到hprof文件進行分析。

3.我們可以點擊
Arrange by packge按包名進行排序或者點擊Filter搜索myApplication。通過觀察可以發(fā)現(xiàn)強制GC之后MemoryPerformanceActivity仍然存在6個對象。我們想要看到的是1個對象,因此這并不是我們預(yù)期想要看到的。

4.點擊MemoryPerformanceActivity查看實例,可以看到每個Aactivity的實際內(nèi)存分配為94000左右,引用深度Depth為3,再通過References查看引用,可以看出來是mCallBacks持有了6個Activity的引用,其Retained Size的大小為570803。最后看到
shadow$_klass_in_CallBackManager,發(fā)現(xiàn)是CallBackManager引用了mCallBacks,點擊右鍵選擇Jump to source跳轉(zhuǎn)到CallBackManager進行問題分析。

5.通過分析我們發(fā)現(xiàn)CallBackManager 是個靜態(tài)類,持有的對象與App生命周期一樣長,因此需要手動將CallBack移除。
override fun onDestroy() {
super.onDestroy()
CallBackManager.removeCallBack(this)
}
5 內(nèi)存溢出
?當申請不到需要的內(nèi)存時就會發(fā)生內(nèi)存溢出(OOM),在開發(fā)過程中我們常見的OOM就有Bitmap加載不合理圖片造成的。我們可以在線下通過ARTHoot去監(jiān)測出不合理的圖片,在線下就將問題暴露出來。
?從錯誤堆棧信息不一定能看出內(nèi)存溢出的準確問題點,因為內(nèi)存溢出可能只是表象,造成內(nèi)存溢出的原因有可能是因為內(nèi)存泄漏,導(dǎo)致可用內(nèi)存越來越少,最后分配不到我們所需的空間,導(dǎo)致內(nèi)存溢出。
6 LeakCanary
?leakcanary是由square開源的框架,可以幫助我們快速發(fā)現(xiàn)內(nèi)存泄漏,減少OOM。集成很簡單,只需要下面這一步,原理就是利用了Provider的Oncreate比Application的生命周期還要早,內(nèi)部實現(xiàn)了Provider并進行框架初始化。
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
}
集成之后Leakcanary就會自動幫我們收集內(nèi)存泄漏信息。

總結(jié)
- MAT和Profiler使用上差不多,因此建議使用Profiler即可,發(fā)現(xiàn)問題可直接跳轉(zhuǎn)到相應(yīng)的代碼位置,提高排查效率。
- 在開發(fā)初期集成LeakCanary去收集內(nèi)存泄漏信息,早發(fā)現(xiàn)早解決。