CursorWindow OOM

2016-10-30  本文已影响374人  客舟求简

错误信息

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的操作。

上一篇下一篇

猜你喜欢

热点阅读