I have gone through and made sure all my Cursors are closed. I am running with strict mode with detectLeakedSqlLiteObjects and detectLeakedClosableObjects and that never gets triggered. But when running on production a few users will crash with this:
android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed.
Could this be happening because the data is too large?
Could it be happening because some third party library is leaving cursors open, like an ad library for example? Are cursors shared between all DBs?
What else should I check?
In short No, you would get a different message:-
A CursorWindow must be able to hold at least one row, if a row is larger than the CursorWindow then you will get a message that indicates the size of the CursorWindow and the size of the data (larger than the former).
e.g.
Window is full: requested allocation 3095146 bytes, free space 2096696 bytes, window size 2097152 bytesSo the row (which contains a column that requires 3095146 bytes) cannot fit into the window with is 2048k (2097152 / 1024) (prior rows/columns have used 456 bytes).
However, it may be that the 456 bytes has a full row or more, in which case that row or rows will be in the CursorWindow.
It may be that then the CursorWindow can accommodate the row that did not fit but then the following row may not fit. So that message may be repeated and even never appear again and all the data is extracted.
If the CursorWindow cannot handle the data then, at least for a BLOB, you would get,the actual failure message something like :-
Here's how this could all happen:-
It should be noted that the CursorWindowSize varies, I believe dependant upon the Android version (used to 1Mb and I believe as large as 4Mb).
A Cursor is effectively a file, there is a limit on the number of files (file handles) that can be open/opened. So although Cursors are not shared. There is a limit of how many files can be open at the same time.
An issue that has arisen has been that cursors have apparently been closed BUT after a loop that was opening cursors for each iteration. So apparently closing BUT only closing the last (along the lines of the "how to simulate cursorwindow allocation" linked below).
Another cause could be that there is simply not enough memory from which to get the 2048k.
Furthermore, perhaps not relevant, is that a CursorWindow is only obtained/allocated if a Cursor is accessed. The following (with the
getCount()commented out) works fine for 4000 cursors.:-With the
getCount()in:-You may find the following of interest:-
Row too big to fit into CursorWindow requiredPos=0, totalRows=1;
Android: How to simulate CursorWindowAllocationException crash
Yes and if possible perhaps a copy of the database file(possibly files if using WAL).
The log should point you to exactly where the failure is occurring, you could then determine the query/table(s) where the issue is occurring and thus be able to reduce the code that needs to be investigated. You could also run tests/extracts on the data e.g. using the SQLite
lengthfunction to determine the length columns.e.g. something based upon
SELECT rowid,length(the_column) AS rowsize FROM the_table GROUP BY rowid ORDER BY rowsize DESC;Perhaps consider the following that will check every column in every row and the accumulated length of all the columns to see if there may be a row or rows that exceed the specified CursorWindow size (again the CursorWindowSize appears to be API dependant).
The
DatabaseHelperclass with the potentially pertinentisCursorRetrievablemethod that will check most tables (will not work on WITHOUT ROWID tables). ThegetAllTrappedmethod is what was used to provide the Blob Too Big log extracts.Here's using the the above in some activity code:-
The log includes (just the first warning at the 4th row ):-