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.jianshu.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,529评论 1 7
  • 前几天整理了Java面试题集合,今天再来整理下Android相关的面试题集合.如果你希望能得到最新的消息,可以关注...
    Boyko阅读 3,714评论 8 135
  • 自换了大盆之后简直一发不可收拾,长得太快了!又要爆盆啦!
    卡蜜娅养肉ing阅读 542评论 0 1
  • 傍晚出门散步,走在乡村田野间的小路上,晚霞照亮了西边的天,我的心情惬意舒畅。无意间,低头发现路旁开放着一堆...
    厚德载物02阅读 1,309评论 1 3
  • 四月的轻寒 浅若一缕薄凉的清风 阳春的细暖 温如一朵粉面的桃花 轻寒邂逅了细暖 缘结成一场多情的春雨 就像一颗冰凉...
    春衫凉阅读 417评论 12 25