亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android檢測Cursor泄漏的原理以及使用方法

2020-04-11 12:34:51
字體:
來源:轉載
供稿:網友
簡介
本文介紹如何在 Android 檢測 Cursor 泄漏的原理以及使用方法,還指出幾種常見的出錯示例。有一些泄漏在代碼中難以察覺,但程序長時間運行后必然會出現異常。同時該方法同樣適合于其他需要檢測資源泄露的情況。

最近發現某蔬菜手機連接程序在查詢媒體存儲(MediaProvider)數據庫時出現嚴重 Cursor 泄漏現象,運行一段時間后會導致系統中所有使用到該數據庫的程序無法使用。另外在工作中也常發現有些應用有 Cursor 泄漏現象,由于需要長時間運行才會出現異常,所以有的此類 bug 很長時間都沒被發現。

但是一旦 Cursor 泄漏累計到一定數目(通常為數百個)必然會出現無法查詢數據庫的情況,只有等數據庫服務所在進程死掉重啟才能恢復正常。通常的出錯信息如下,指出某 pid 的程序打開了 866 個 Cursor 沒有關閉,導致了 exception:
復制代碼 代碼如下:

3634 3644 E JavaBinder: *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
3634 3644 E JavaBinder: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=866 (# cursors opened by pid 1565=866)
3634 3644 E JavaBinder: at android.database.CursorWindow.(CursorWindow.java:104)
3634 3644 E JavaBinder: at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
3634 3644 E JavaBinder: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:147)
3634 3644 E JavaBinder: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:141)
3634 3644 E JavaBinder: at android.database.CursorToBulkCursorAdaptor.getBulkCursorDescriptor(CursorToBulkCursorAdaptor.java:143)
3634 3644 E JavaBinder: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:118)
3634 3644 E JavaBinder: at android.os.Binder.execTransact(Binder.java:367)
3634 3644 E JavaBinder: at dalvik.system.NativeStart.run(Native Method)

1. Cursor 檢測原理
在 Cursor 對象被 JVM 回收運行到 finalize() 方法的時候,檢測 close() 方法有沒有被調用,此辦法在 ContentResolver 里面也得到應用。簡化后的示例代碼如下:
復制代碼 代碼如下:

import android.database.Cursor;
import android.database.CursorWrapper;
import android.util.Log;
public class TestCursor extends CursorWrapper {
private static final String TAG = "TestCursor";
private boolean mIsClosed = false;
private Throwable mTrace;
public TestCursor(Cursor c) {
super(c);
mTrace = new Throwable("Explicit termination method 'close()' not called");
}
@Override
public void close() {
mIsClosed = true;
}
@Override
public void finalize() throws Throwable {
try {
if (mIsClosed != true) {
Log.e(TAG, "Cursor leaks", mTrace);
}
} finally {
super.finalize();
}
}
}

然后查詢的時候,把 TestCursor 作為查詢結果返回給 APP:
1 return new TestCursor(cursor); // cursor 是普通查詢得到的結果,例如從 ContentProvider.query()
該方法同樣適合于所有需要檢測顯式釋放資源方法沒有被調用的情形,是一種通用方法。但在 finalize() 方法里檢測需要注意
優點:準確。因為該資源在 Cursor 對象被回收時仍沒被釋放,肯定是發生了資源泄露。
缺點:依賴于 finalize() 方法,也就依賴于 JVM 的垃圾回收策略。例如某 APP 現在有 10 個 Cursor 對象泄露,并且這 10 個對象已經不再被任何引用指向處于可回收狀態,但是 JVM 可能并不會馬上回收(時間不可預測),如果你現在檢查不能夠發現問題。另外,在某些情況下就算對象被回收 finalize() 可能也不會執行,也就是不能保證檢測出所有問題。關于 finalize() 更多信息可以參考《Effective Java 2nd Edition》的 Item 7: Avoid Finalizers

2. 使用方法
對于 APP 開發人員
從 GINGERBREAD 開始 Android 就提供了 StrictMode 工具協助開發人員檢查是否不小心地做了一些不該有的操作。使用方法是在 Activity 里面設置 StrictMode,下面的例子是打開了檢查泄漏的 SQLite 對象以及 Closeable 對象(普通 Cursor/FileInputStream 等)的功能,發現有違規情況則記錄 log 并使程序強行退出。
復制代碼 代碼如下:

import android.os.StrictMode;
public class TestActivity extends Activity {
private static final boolean DEVELOPER_MODE = true;
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setVMPolicy(new StrictMode.VMPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
}

對于 framework 開發人員
如果是通過 ContentProvider 提供數據庫數據,在 ContentResolver 里面已有 CloseGuard 類實行類似檢測,但需要自行打開(上例也是打開 CloseGuard):
1 CloseGuard.setEnabled(true);更值得推薦的辦法是按照本文第一節中的檢測原理,在 ContentResolver 內部類 CursorWrapperInner 里面加入。其他需要檢測類似于資源泄漏的,同樣可以使用該檢測原理。

3. 容易出錯的地方
忘記調用 close() 這種低級錯誤沒什么好說的,這種應該也占不小的比例。下面說說不太明顯的例子。
提前返回
有時候粗心會犯這種錯誤,在 close() 調用之前就 return 了,特別是函數比較大邏輯比較復雜時更容易犯錯。這種情況可以通過把 close() 放在 finally 代碼塊解決
復制代碼 代碼如下:

private void method() {
Cursor cursor = query(); // 假設 query() 是一個查詢數據庫返回 Cursor 結果的函數
if (flag == false) { // ??!提前返回
return;
}
cursor.close();
}

類的成員變量
假設類里面有一個在類全局有效的成員變量,在方法 A 獲取了查詢結果,后面在其他地方又獲取了一次查詢結果,那么第二次查詢的時候就應該先把前面一個 Cursor 對象關閉。
復制代碼 代碼如下:

public class TestCursor {
private Cursor mCursor;
private void methodA() {
mCursor = query();
}
private void methodB() {
// !!必須先關閉上一個 cursor 對象
mCursor = query();
}
}

注意:曾經遇到過有人對 mCursor 感到疑惑,明明是同一個變量為什么還需要先關閉?首先 mCursor 是一個 Cursor 對象的引用,在 methodA 時 mCursor 指向了 query() 返回的一個 Cursor 對象 1;在 methodB() 時它又指向了返回的另外一個 Cursor 對象 2。在指向 Cursor 對象 2 之前必須先關閉 Cursor 對象 1,否則就出現了 Cursor 對象 1 在 finalize() 之前沒有調用 close() 的情況。
異常處理
打開和關閉 Cursor 之間的代碼出現 exception,導致沒有跑到關閉的地方:
復制代碼 代碼如下:

try {
Cursor cursor = query();
// 中間省略某些出現異常的代碼
cursor.close();
} catch (Exception e) {
// ??!出現異常沒跑到 cursor.close()
}

這種情況應該把 close() 放到 finally 代碼塊里面:
復制代碼 代碼如下:

Cursor cursor = null;
try {
cursor = query();
// 中間省略某些出現異常的代碼
} catch (Exception e) {
// 出現異常
} finally {
if (cursor != null)
cursor.close();
}
 
4. 總結思考
在 finalize() 里面檢測是可行的,且基本可以滿足需要。針對 finalize() 執行時間不確定以及可能不執行的問題,可以通過記錄目前打開沒關閉的 Cursor 數量來部分解決,超過一定數目發出警告,兩種手段相結合。

還有沒有其他檢測辦法呢?有,在 Cursor 構造方法以及 close() 方法添加 log,運行一段時間后檢查 log 看哪個地方沒有關閉。簡化代碼如下:
復制代碼 代碼如下:

import android.database.Cursor;
import android.database.CursorWrapper;
import android.util.Log;
public class TestCursor extends CursorWrapper {
private static final String TAG = "TestCursor";
private Throwable mTrace;
public TestCursor(Cursor c) {
super(c);
mTrace = new Throwable("cusor opened here");
Log.d(TAG, "Cursor " + this.hashCode() + " opened, stacktrace is: ", mTrace);
}
@Override
public void close() {
mIsClosed = true;
Log.d(TAG, "Cursor " + this.hashCode() + " closed.");
}
}

檢查時看某個 hashCode() 的 Cursor 有沒有調用過 close() 方法,沒有的話說明資源有泄露。這種方法優點是同樣準確,且更可靠。缺點是需要檢查大量 log,且打開/關閉的地方可能相距較遠,如果不寫個小腳本分析人工看的話會比較痛苦;另外必須 APP 完全退出后才能檢查,因為后臺運行時某些 Cursor 還在正常使用。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕亚洲图片| 久久精品视频亚洲| 欧美一区二区三区四区在线| 美日韩精品视频免费看| 久久成人在线视频| 国产精品久久久久久久久久新婚| 91久久久久久久一区二区| 国产精品美腿一区在线看| 午夜精品在线观看| 色婷婷综合成人av| 国产精品羞羞答答| 日韩精品亚洲视频| 亚洲高清色综合| 91在线免费观看网站| 亚洲成人国产精品| 狠狠躁夜夜躁久久躁别揉| 久久久久久亚洲精品不卡| 中文字幕在线国产精品| 国产精品免费小视频| 亚洲老头同性xxxxx| 国产成人啪精品视频免费网| 在线电影av不卡网址| 91麻豆国产语对白在线观看| 久热精品视频在线免费观看| 国产精品久久久久久久久久久久久| 91成人性视频| 欧美怡红院视频一区二区三区| 国产免费一区二区三区香蕉精| 国产日本欧美在线观看| 欧美国产日韩一区二区在线观看| 国产精品18久久久久久首页狼| 亚洲综合色av| 亚洲一区亚洲二区| 日韩av一区在线观看| 成人精品在线观看| 国产99视频在线观看| 成人a级免费视频| 日韩视频免费看| 久久影院资源站| 高清一区二区三区四区五区| 国内精品一区二区三区四区| 国产亚洲精品美女久久久| 国产精品成人久久久久| y97精品国产97久久久久久| 欧美精品午夜视频| 成人亚洲欧美一区二区三区| 国产精品久久国产精品99gif| 精品国产乱码久久久久久天美| 自拍偷拍亚洲欧美| www日韩中文字幕在线看| 亚洲男人的天堂网站| 少妇高潮久久久久久潘金莲| 国产精品久久久久久婷婷天堂| 精品女厕一区二区三区| 国产日韩欧美综合| 亚洲欧美精品一区| 91久久精品美女高潮| 国产一区红桃视频| 亚洲色图色老头| 91精品视频免费看| 最新亚洲国产精品| www.久久色.com| 亚洲色图校园春色| 国产91在线播放| 欧美午夜激情在线| 亚洲 日韩 国产第一| 国产精品2018| 97精品国产91久久久久久| 国产精品丝袜一区二区三区| 国产91在线高潮白浆在线观看| 国产精品啪视频| 亚洲欧美激情四射在线日| xvideos亚洲| 91精品久久久久久久久不口人| 国产精品aaaa| 久久久久国产精品免费| 久久色在线播放| 亚洲性线免费观看视频成熟| 国产一区二区三区三区在线观看| 成人午夜激情免费视频| 国产日韩欧美91| 欧美另类在线播放| 欧美国产中文字幕| 欧美人与性动交| 亚洲国产成人精品电影| 91视频免费网站| 亚洲爱爱爱爱爱| 亚洲国产成人久久综合一区| 久久好看免费视频| 亚洲毛片在线看| 亚洲精品一区在线观看香蕉| 亚洲美女在线观看| 91精品视频免费观看| 日韩性生活视频| 欧美专区中文字幕| 亚洲欧美精品伊人久久| 国产区精品在线观看| 日韩av影院在线观看| 国产精品观看在线亚洲人成网| 成人网在线免费观看| 亚洲一区二区三区久久| 国产精品成人播放| 国产一区二区av| 91免费的视频在线播放| 亚洲国产精彩中文乱码av在线播放| 一本色道久久88综合亚洲精品ⅰ| 欧美一级视频免费在线观看| 亚洲区在线播放| 麻豆国产va免费精品高清在线| 69精品小视频| 欧美激情视频在线免费观看 欧美视频免费一| 久久天天躁狠狠躁老女人| 久久理论片午夜琪琪电影网| 国产在线不卡精品| 日韩在线中文字| 亚洲欧美国产精品专区久久| 欧美精品18videos性欧| 国产精品美女主播在线观看纯欲| xxxx欧美18另类的高清| 久热精品视频在线| 91网站免费看| 欧洲精品毛片网站| 97免费视频在线| 国内外成人免费激情在线视频| 欧美韩国理论所午夜片917电影| 日本一区二三区好的精华液| 欧美日韩精品在线播放| 欧美日韩一区二区在线| 欧美视频中文字幕在线| 日韩激情片免费| 亚洲欧洲国产一区| 夜夜嗨av一区二区三区免费区| 国产精品视频大全| 色噜噜国产精品视频一区二区| 亚洲精品suv精品一区二区| 久久免费国产视频| 国产精品爽爽爽| 国产不卡视频在线| 久久久久久久激情视频| 日韩av在线天堂网| 欧美成人精品一区| 中国人与牲禽动交精品| 久久精品91久久香蕉加勒比| 国产噜噜噜噜噜久久久久久久久| 亚洲www永久成人夜色| 亚洲黄色成人网| 久久久精品免费视频| 国产成人精品久久久| 欧美激情第99页| 91欧美视频网站| 国产精品精品一区二区三区午夜版| 国产成人av网| 久久深夜福利免费观看| 亚洲视频欧美视频| 亚洲人成网站免费播放| 国产色婷婷国产综合在线理论片a| 精品女同一区二区三区在线播放| 国产精品久久久久久久app| 美女啪啪无遮挡免费久久网站| 精品久久在线播放| 日韩视频免费大全中文字幕| 精品国产区一区二区三区在线观看| 亚洲国产高清自拍| 亚洲性生活视频|