title: Android 內(nèi)存泄露的幾中場景
date: 2016-11-22 13:57
tags:
- Android
Android 內(nèi)存泄漏
java 存在一個垃圾回收機(jī)制,發(fā)生泄漏的原因就是應(yīng)該被回收的垃圾沒有被回收,這種情況就叫做內(nèi)存泄漏
解決內(nèi)存泄漏的方法的思路:讓不易回收的內(nèi)存可以在不需要繼續(xù)使用單情況下被系統(tǒng)回收掉。
怎么做在能讓某些內(nèi)存可以被及時回收呢?這里就需要了解 java 的一個知識點浪汪,這就是引用類型。java 分 四 種引用類型,分別是:強(qiáng)引用袱耽,軟引用,弱引用干发,虛引用朱巨。這從這四種引用點名稱可以推測,系統(tǒng)對于不用類型引用有不同的回收機(jī)制枉长。為方便對比列出下方的表格
引用類型 | 回收條件 | 發(fā)生泄漏可能性 |
---|---|---|
強(qiáng)引用 | 不回收 | 可能 |
軟引用 | 內(nèi)存不足時回收 | 不可能 |
弱引用 | 一定回收 | 不可能 |
虛引用 | 不回收 | 可能 |
合理點使用不同的引用類型蔬崩,可以避免出現(xiàn)內(nèi)存泄露的情況
在Android開發(fā)中,出現(xiàn)內(nèi)存泄露我認(rèn)為可以分為兩大部分搀暑,一部分是 java 相關(guān)的沥阳,另一部分是 Android Api使用容易出現(xiàn)內(nèi)存泄露
常見有以下場景
1. 單例模式
單例模式生成的靜態(tài)對象因為生命周期和應(yīng)用的進(jìn)程一致,一般只有當(dāng)應(yīng)用退出或者運行的進(jìn)程被結(jié)束自点,對象才能結(jié)束對象的生命周桐罕。例如當(dāng)一個單例對象引用一個 activity 變量,即便 activity 可能已經(jīng)退出了桂敛,但是因為單例對象還持有 activity 功炮,所以系統(tǒng)不能回收這個 activity 造成了內(nèi)存泄露
解決方案
使用弱應(yīng)用,例如
private WeakReference<Activity> wr = null;
wr = new WeakReference<Activity>(myActivity);
2. 匿名內(nèi)部類使用(Threads , TimerTasks)
非靜態(tài)內(nèi)部類术唬,匿名類閱讀薪伏,可以輕易訪問外圍類的變量,也就是說匿名內(nèi)部類可以持有外部類的的變量粗仓,當(dāng)外部類的變量可以被回收的時候嫁怀,當(dāng)時因為內(nèi)部類持有外部類的引用,這樣就造成了內(nèi)存泄露借浊。但是靜態(tài)內(nèi)部類就不會引用外部變量
解決方案
1.使用靜態(tài)內(nèi)部類
2.用弱引用就是引用到的變量
3.可以的話塘淑,在結(jié)束外部類時及時關(guān)閉匿名內(nèi)部類(Thread)
3. context 的使用
Context 是 Android 開發(fā)中經(jīng)常傳遞的變量,但是某些情況下本該被回收的 Context 卻因為某些對象依然持有 Context 的引用蚂斤,進(jìn)而發(fā)生內(nèi)存泄露
解決方案
1.使用 ApplicationContext 代替 Context 存捺,因為 ApplicationContext 的生命周期和應(yīng)用(進(jìn)程)一樣長。
2.對于 Context 變量慎用 Static 修飾曙蒸,
4. handler 的使用
Handler 用于延時的一個作用捌治,熟悉 Handler 機(jī)制的都應(yīng)該清楚岗钩,我們會把一下需要的一些操作放在 Messagequeue 里面,等待 用戶的 handler 對象按隊列順序發(fā)送消息肖油。
會存在這樣一個場景凹嘲,當(dāng)一個 Activity 需要銷毀,但是 MessageQueue 還存在一些消息為處理构韵,消息持有 Handler 引用周蹭,Handler 持有外部類的引用,這時 Activity 就無法正称;郑回收了凶朗。這種情況和非靜態(tài)內(nèi)部類引起的原因差不多。
解決方案
1.把 Handler 實現(xiàn)做一個獨立的類或者使用靜態(tài)修飾
2.在 Handler 內(nèi)部調(diào)用到 acivity 等外部變量是可以用弱引用修飾作為 activity 引用類型
5. Cursor Bitmap Stream 沒及時釋放
打開資源文件显拳,會把文件緩存在內(nèi)存和 jvm 虛擬機(jī)中棚愤,在使用結(jié)束后如果沒有 close() 則會發(fā)生內(nèi)存泄露。
解決方案
1.使用結(jié)束后及時調(diào)用 close()
6. 監(jiān)聽器的沒及時注銷(Sensor Manager)
Android 系統(tǒng)提供了一些服務(wù)杂数,要獲取這些服務(wù)的對象都要使用到 Context 這個變量
當(dāng)一個 activity 關(guān)閉后宛畦,這些系統(tǒng)級別的服務(wù)都會繼續(xù)持有 Context 引用
解決方案
1.在退出 Activity 后及時注銷監(jiān)聽器
2.使用 ApplicationContext 代替 Context
7. listView 的 adpater 沒使用 ConvertView 緩存
ListView 會緩存一部分 View ,沒用使用 getview() 里面的參數(shù) ConvertView
8. 靜態(tài)集合對象沒有清理
靜態(tài)集合里數(shù)據(jù)多揍移,生命周期與應(yīng)用一樣長次和,但是依然占據(jù)了一部分內(nèi)存,不需要時沒有進(jìn)行清理的動作
解決方案
1.退出后 clear 掉集合里面的所有數(shù)據(jù)那伐,然后賦值 null
9. webview 沒有及時釋放
webview 使用完畢后一樣需要 destroy 掉踏施,否者一直常駐內(nèi)存
解決方案
因為 webView 耗費大量內(nèi)存,可以為 WebView 分配一個獨立的線程罕邀,也主線程做通訊畅形。不需要時也要記得銷毀掉。
10.監(jiān)聽器
使用監(jiān)聽者模式诉探,我們會添加一些監(jiān)聽器日熬,但是移除被監(jiān)聽對象時,往往忘記取消設(shè)置的監(jiān)聽器