CursorWindow OOM

錯誤信息

08-30 20:27:36.751 E/CursorWindow(  760): Could not allocate CursorWindow '/data/data/com.android.providers.media/databases/external.db' of size 2097152 due to error -12.
08-30 20:27:36.771 E/JavaBinder(  760): *** Uncaught remote exception!  (Exceptions are not yet supported across processes.)
08-30 20:27:36.771 E/JavaBinder(  760): android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=781 (# cursors opened by pid 3105=781)
08-30 20:27:36.771 E/JavaBinder(  760):  at android.database.CursorWindow.<init>(CursorWindow.java:104)
08-30 20:27:36.771 E/JavaBinder(  760):  at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
08-30 20:27:36.771 E/JavaBinder(  760):  at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:162)
08-30 20:27:36.771 E/JavaBinder(  760):  at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
08-30 20:27:36.771 E/JavaBinder(  760):  at android.database.CursorToBulkCursorAdaptor.count(CursorToBulkCursorAdaptor.java:184)
08-30 20:27:36.771 E/JavaBinder(  760):  at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:117)
08-30 20:27:36.771 E/JavaBinder(  760):  at android.os.Binder.execTransact(Binder.java:338)
08-30 20:27:36.771 E/JavaBinder(  760):  at dalvik.system.NativeStart.run(Native Method)

錯誤原因

CursorWindow緩存數據達到最大限制(2M不同的機器和SQLite版本其值可能不同)后,仍有查詢結果集需要緩存,在申請內存分配時申請失敗發生了OOM內存溢出;SQLite查詢出的數據集cursor,都由native層的CursorWindow進行數據管理,包括內存空間的申請和數據的填充。CursorWindow實際上是共享內存的抽象,以實現跨進程,跨應用數據共享(ContentProvider作為數據通道,也支持跨進程,跨應用的數據訪問)
在ContentProvider端透過SQLiteDatabase的封裝查詢到的數據集保存在CursorWindow所指向的共享內存中,然后通過Binder把這片共享內存傳遞到ContentResolver端,即查詢端。這樣客戶就可以通過Cursor來訪問這塊共享內存中的數據集了。

解決辦法

保證CursorWindow不會達到最大限制):
1.只查詢需要的字段;
根據UI顯示需要,或實際需要查詢的字段進行查詢,盡量不會表查詢
2.二進制文件不要存在數據庫中;
數據庫僅適用于保存一些較短文字,整數,布爾,浮點數等一些,易于查詢和操作的輕量級的數據,目的也是在于快速搜索和查詢。對于像圖片,較長的文字(如文章)等大數據,最好直接以文件形式存儲在硬盤中,然后在數據庫保存它們的訪問路徑
3.對于大數據量的查詢采用分段查詢方式;
無論表中的一條記錄數據量如何的小,當條數達到5000級或者萬級或者更多的時候,還是會達到最大的限制
4.正確的關閉Cursor,釋放CursorWindow中不用的資源(需手動調用釋放native中的資源,類似3.0之前的Bitmap需要手動釋放。調用close的必要性:http://www.lxweimin.com/p/3b433ed25aaf

     Cursor c;
     try { 
         c = queryCursor(); 
         int a = c.getInt(1); 
         ......
         // 如果出錯,后面的cursor.close()將不會執行
         //c.close(); 
     } catch (Exception e) { 
     } finally{
         if (c != null) {
             c.close();
         }
     } 

如果你的Cursor需要在Activity的不同的生命周期方法中打開和關閉,那么一般可以這樣做:
在onCreate()中打開,在onDestroy()中關閉;
在onStart() 中打開,在onStop() 中關閉;
在onResume()中打開,在onPause() 中關閉;
即要在成對的生命周期方法中打開/關閉

如果程序中使用了CursorAdapter(例如Music),那么可以使用它的changeCursor(Cursor cursor)方法同時完成關閉舊Cursor使用新Cursor的操作。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 由于android系統中應用程序之間不能共享內存。因此,在不同應用程序之間交互數據(跨進程通訊)就稍微麻煩一些。在...
    Ten_Minutes閱讀 8,507評論 1 7
  • 前幾天整理了Java面試題集合,今天再來整理下Android相關的面試題集合.如果你希望能得到最新的消息,可以關注...
    Boyko閱讀 3,681評論 8 135
  • 一.菜單Menu 1.OptionsMenu 選項菜單 也叫系統菜單,右上角的三點 (1)高版本的菜單 ...
    chaohx閱讀 1,052評論 0 7
  • 自換了大盆之后簡直一發不可收拾,長得太快了!又要爆盆啦!
    卡蜜婭養肉ing閱讀 529評論 0 1
  • 傍晚出門散步,走在鄉村田野間的小路上,晚霞照亮了西邊的天,我的心情愜意舒暢。無意間,低頭發現路旁開放著一堆...
    厚德載物02閱讀 1,304評論 1 3