Android效能優化篇之記憶體優化--記憶體洩漏
引言
1. Android效能優化篇之記憶體優化--記憶體洩漏
2.Android效能優化篇之記憶體優化--記憶體優化分析工具
3.Android效能優化篇之UI渲染效能優化
4.Android效能優化篇之計算效能優化
5.Android效能優化篇之電量優化(1)——電量消耗分析
6.Android效能優化篇之電量優化(2)
7.Android效能優化篇之網路優化
8.Android效能優化篇之Bitmap優化
9.Android效能優化篇之圖片壓縮優化
10.Android效能優化篇之多執行緒併發優化
11.Android效能優化篇之資料傳輸效率優化
12.Android效能優化篇之程式啟動時間效能優化
13.Android效能優化篇之安裝包效能優化
14.Android效能優化篇之服務優化
介紹
今天主要是講記憶體洩漏的產生原因分析,常見的導致記憶體洩漏的示例,以及記憶體洩漏優化的方法。中間穿插著有關java虛擬機器記憶體管理,記憶體分配策略,垃圾收集器的相關知識點。下面就來列出今天講解的大體流程。
講解流程:
1.什麼是記憶體洩漏?
2.android中導致記憶體洩漏的主要幾個點
3.java虛擬機器記憶體管理
4.java記憶體幾種分配策略?
5.垃圾收集器是如何判斷物件是否可回收?
6.什麼是記憶體抖動?
7.記憶體抖動產生的原因?
8.android中4種引用
9.常見的導致記憶體洩漏的示例
下面我們就以上面幾個知識點來進行逐一的分析:
1.什麼是記憶體洩漏?
當一個物件已經不需要在使用了,本應該被回收,而另一個正在使用的物件持有它的引用,導致物件不能被回收。因為不能被及時回收的本該被回收的記憶體,就產生了記憶體洩漏。如果記憶體洩漏太多會導致程式沒有辦法申請記憶體,最後出現記憶體溢位的錯誤。
2.android中導致記憶體洩漏的主要幾個點
android開發中經常出現的點,我有隻有了解了,才能更好的避免。
- 使用單例模式
- 使用匿名內部類
- 使用非同步事件處理機制Handler
- 使用靜態變數
- 資源未關閉
- 設定監聽
- 使用AsyncTask
- 使用Bitmap
上面就是我列出的幾個常出現記憶體洩漏的幾個點,下面我們將一一解讀。
3.java虛擬機器記憶體管理
java虛擬機器記憶體分為虛擬機器棧,本地方法棧,程式計數器,堆,方法區這幾個模組,下面我們就來分析下各個模組。
(1).虛擬機器棧
- 虛擬機器棧主要的作用就是為執行java方法服務的,是Java方法執行的動態記憶體模型。
* 會導致棧記憶體溢位(StackOverFlowError)
(2).本地方法棧
- 為執行native方法服務的,其他和虛擬機器棧一樣
(3).程式計數器
- 是當前執行緒執行的位元組碼行號指示器
- 處於執行緒獨佔區
- 如果是執行的是java程式碼,當前值為位元組碼指令的地址,如果是Native,值為undefined
(4).堆
- 存放物件的例項
- 垃圾收集器管理的主要區域
- 分代管理物件
- 會導致記憶體溢位(OutOfMemoryError)
(5).方法區
- 存放虛擬機器載入的類資訊,常量,靜態變數,編譯後的程式碼和資料
- GC主要對方法區進行常量回收和類解除安裝
- 會出現記憶體溢位(OutOfMemoryError)
4.java記憶體幾種分配策略?
可以結合上面的記憶體分配模型,能很好的理解。
(1).靜態的
- 靜態儲存區:記憶體在程式編譯期間就已經分配完成,一般來說,這個區域在程式執行期間一直處在
- 它主要儲存靜態資料,全域性靜態資料和常量
(2).棧式的
- 執行方法時,儲存區域性變數(編譯期間,已經確定佔用記憶體大小),運算元,動態連結,方法出口
(3).堆式的
- 也叫動態記憶體分配,主要儲存物件例項,以及已經被載入類的Class物件(用於反射)
5.垃圾收集器是如何判斷物件是否可回收?
我們知道記憶體洩漏的原因是應該被回收的物件,不能被及時回收,那麼GC是如何來判斷物件是否為垃圾物件呢?
判斷的方式有兩個:
* 引用計數
物件被引用,引用計數器加1,反之減一,只有引用計數為0,那麼這個物件為垃圾物件
* 可達性
從GCRoot節點物件開始,看是否可以訪問到此物件,如果沒有訪問到則為垃圾物件
可以作為GCRoot物件有以下幾種:
- 虛擬機器棧中的區域性變數
- 本地方法棧中的引用物件
- 方法區中的常量引用物件
- 方法區中的類屬性引用物件
在native層和早期的虛擬機器一般使用引用計數,但是現在的java虛擬機器大多使用的是可達性。
6.什麼是記憶體抖動?
堆記憶體都有一定的大小,能容納的資料是有限制的,當Java堆的大小太大時,垃圾收集會啟動停止堆中不再應用的物件,來釋放記憶體。當在極短時間內分配給物件和回收物件的過程就是記憶體抖動。
7.記憶體抖動產生的原因?
從術語上來講就是極短時間內分配給物件和回收物件的過程。
一般多是在迴圈語句中建立臨時物件,在繪製時配置大量物件或者執行動畫時建立大量臨時物件
記憶體抖動會帶來UI的卡頓,因為大量的物件建立,會很快消耗剩餘記憶體,導致GC回收,GC會佔用大量的幀繪製時間,從而導致UI卡頓,關於UI卡頓會在後面章節講到。
8.android中4種引用
(1).StrongReference強引用
從不被回收,java虛擬機器停止時,才終止
(2).SoftReference強引用
當記憶體不足時,會主動回收,使用SoftReference使用結合ReferenceQueue構造有效期短
(3).WeakReference強引用
每次垃圾回收時,被回收
(4).PhatomReference強引用
每次垃圾回收時,被回收.結合ReferenceQueue來跟蹤物件被垃圾回收器回收的活動
9.常見的導致記憶體洩漏的示例
(1).使用單例模式
private static ComonUtil mInstance = null;
private Context mContext = null;
public ComonUtil(Context context) {
mContext = context;
}
public static ComonUtil getInstance(Context context) {
if (mInstance == null) {
mInstance = new ComonUtil(context);
}
return mInstance;
}
使用:
ComonUtil mComonUtil = ComonUtil.getInstance(this);
我們看到上面的程式碼就是我們平時使用的單例模式,當然這裡沒有考慮執行緒安全,請忽略。當我們傳遞進來的是Context,那麼當前物件就會持有第一次例項化的Context,如果Context是Activity物件,那麼就會產生記憶體洩漏。因為當前物件ComonUtil是靜態的,生命週期和應用是一樣的,只有應用退出才會釋放,導致Activity不能及時釋放,帶來記憶體洩漏。
怎麼解決呢?
常見的有兩種方式,第一就是傳入ApplicationContext,第二CommonUtil中取context.getApplicationContext()。
public ComonUtil(Context context) {
mContext = context.getApplicationContext();
}
(2).使用非靜態內部類
/**
* 非靜態內部類
*/
public void createNonStaticInnerClass(){
CustomThread mCustomThread = new CustomThread();
mCustomThread.start();
}
public class CustomThread extends Thread{
@Override
public void run() {
super.run();
while (true){
try {
Thread.sleep(5000);
Log.i(TAG,"CustomThread ------- 列印");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
我們就以執行緒為例,當Activity呼叫了createNonStaticInnerClass方法,然後退出當前Activity時,因為執行緒還在後臺執行且當前執行緒持有Activity引用,只有等到執行緒執行完畢,Activitiy才能得到釋放,導致記憶體洩漏。
常用的解決方法有很多,第一把執行緒類宣告為靜態的類,如果要用到Activity物件,那麼就作為引數傳入且為WeakReference,第二在Activity的onDestroy時,停止執行緒的執行。
public static class CustomThread extends Thread{
private WeakReference<MainActivity> mActivity;
public CustomThread(MainActivity activity){
mActivity = new WeakReference<MainActivity>(activity)
}
}
(3).使用非同步事件處理機制Handler
/**
* 非同步訊息處理機制 -- handler機制
*/
public void createHandler(){
mHandler.sendEmptyMessage(0);
}
public Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//處理耗時操作
return false;
}
});
這個應該是我們平時使用最多的一種方式,如果當handler中處理的是耗時操作,或者當前訊息佇列中訊息很多時,那當Activity退出時,當前message中持有handler的引用,handler又持有Activity的引用,導致Activity不能及時的釋放,引起記憶體洩漏的問題。
解決handler引起的記憶體洩漏問題常用的兩種方式:
1.和上面解決Thread的方式一樣,
2.在onDestroy中呼叫mHandler.removeCallbacksAndMessages(null)
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
(4).使用靜態變數
同單例引起的記憶體洩漏。
(5).資源未關閉
常見的就是資料庫遊標沒有關閉,物件檔案流沒有關閉,主要記得關閉就OK了。
(6).設定監聽
常見的是在觀察者模式中出現,我們在退出Acviity時沒有取消監聽,導致被觀察者還持有當前Activity的引用,從而引起記憶體洩漏。
常見的解決方法就是在onPause中注消監聽
(7).使用AsyncTask
public AsyncTask<Object, Object, Object> mTask = new AsyncTask<Object, Object, Object>() {
@Override
protected Object doInBackground(Object... params) {
//耗時操作
return null;
}
@Override
protected void onPostExecute(Object o) {
}
};
和上面同樣的道理,匿名內部類持有外部類的引用,AsyncTask耗時操作導致Activity不能及時釋放,引起記憶體洩漏。
解決方法同上:
1.宣告為靜態類,
2.在onPause中取消任務
(8).使用Bitmap
我們知道當bitmap物件沒有被使用(引用),gc會回收bitmap的佔用記憶體,當時這邊的記憶體指的是java層的,那麼本地記憶體的釋放呢?我們可以通過呼叫bitmap.recycle()來釋放C層上的記憶體,防止本地記憶體洩漏
上面就是這章要將的所有內容,常見的記憶體洩漏的例子也看了,但是我們怎麼知道是否記憶體洩漏了呢,或者說怎麼去發現我們的專案中有記憶體洩漏呢?下一章就來結合上面的例子通過記憶體分析工具來具體的分析。
相關文章
- 效能優化——記憶體洩漏(1)入門篇優化記憶體
- Android 效能優化之記憶體優化Android優化記憶體
- 1.記憶體優化(一)記憶體洩漏記憶體優化
- iOS效能優化 - 工具Instruments之Leaks記憶體洩漏iOS優化記憶體
- Linux 效能優化之 記憶體 篇Linux優化記憶體
- 記憶體洩漏與排查流程——安卓效能優化記憶體安卓優化
- Android記憶體洩漏監控和優化技巧總結Android記憶體優化
- Android記憶體優化之圖片優化Android記憶體優化
- Android記憶體優化Android記憶體優化
- Android深度效能優化--記憶體優化(一篇就夠)Android優化記憶體
- Android記憶體洩漏Android記憶體
- Android 記憶體洩漏Android記憶體
- Android Note - 記憶體優化Android記憶體優化
- android效能評測與優化-記憶體Android優化記憶體
- Android效能最佳化之記憶體洩露Android記憶體洩露
- 淺談Android記憶體優化Android記憶體優化
- Android記憶體優化全解析Android記憶體優化
- Linux效能優化實戰記憶體篇(五)Linux優化記憶體
- Android Handler機制之記憶體洩漏Android記憶體
- 【記憶體洩漏和記憶體溢位】JavaScript之深入淺出理解記憶體洩漏和記憶體溢位記憶體溢位JavaScript
- Android效能優化,Startalk會話頁GIF記憶體優化實踐Android優化會話記憶體
- Java記憶體洩漏、效能優化、當機死鎖的N種姿勢Java記憶體優化
- iOS 使用Instruments優化記憶體效能iOS優化記憶體
- Android記憶體洩漏場景Android記憶體
- JavaScript之記憶體溢位和記憶體洩漏JavaScript記憶體溢位
- 關於redis記憶體分析,記憶體優化Redis記憶體優化
- Android效能優化:手把手帶你全面實現記憶體優化Android優化記憶體
- Android 是如何管理 App 記憶體的 — Android 記憶體優化第二彈AndroidAPP記憶體優化
- JavaScript之記憶體洩漏【四】JavaScript記憶體
- Android備忘錄《記憶體洩漏》Android記憶體
- Android中的記憶體洩漏模式Android記憶體模式
- Android中常見的記憶體洩漏Android記憶體
- 初步探究Android記憶體洩漏(1)Android記憶體
- android記憶體管理機制與優化Android記憶體優化
- 記憶體優化相關記憶體優化
- js記憶體洩漏JS記憶體
- jvm 記憶體洩漏JVM記憶體
- Java記憶體洩漏Java記憶體