Ошибка выделения окна курсора базы данных Android SQLite размером 2048 КБ

52

У меня есть подпрограмма, которая запускает разные запросы к базе данных SQLite много раз в секунду. Через некоторое время я получу ошибку

"android.database.CursorWindowAllocationException: - Cursor window allocation of 2048 kb failed. # Open Cursors = " отображаются в LogCat.

У меня было использование памяти журнала приложений, и действительно, когда использование достигает определенного предела, я получаю эту ошибку, подразумевая, что она заканчивается. Моя интуиция подсказывает мне, что механизм базы данных создает новый буфер (CursorWindow) каждый раз, когда я запускаю запрос, и хотя я отмечаю курсоры .close(), ни сборщик мусора, ни SQLiteDatabase.releaseMemory() достаточно быстро освобождают память, Я думаю, что решение может заключаться в том, чтобы "заставить" базу данных всегда записывать в один и тот же буфер, а не создавать новые, но мне не удалось найти способ сделать это. Я попытался создать экземпляр собственного CursorWindow и попытался настроить его, а SQLiteCursor - безрезультатно.

¿Любые идеи?

EDIT: повторите пример запроса кода от @GrahamBorland:

public static CursorWindow cursorWindow = new CursorWindow("cursorWindow"); 
public static SQLiteCursor sqlCursor;
public static void getItemsVisibleArea(GeoPoint mapCenter, int latSpan, int lonSpan) {
query = "SELECT * FROM Items"; //would be more complex in real code
sqlCursor = (SQLiteCursor)db.rawQuery(query, null);
sqlCursor.setWindow(cursorWindow);
}

В идеале я хотел бы иметь возможность .setWindow() перед тем, как дать новый запрос, и каждый раз, когда я получаю новые данные, данные помещаются в один и тот же CursorWindow.

  • 0
    Я не знаю, в чем проблема .. :) но я использую, чтобы сделать класс SQLiteOpenHelper синглтон. Так что я никогда не обнаруживал таких проблем.
  • 0
    Нет, я не использую SQLiteOpenHelper, я создаю статический класс DataAccess, который содержит базу данных SQLiteDatabase. Это работает нормально, и я сомневаюсь, что проблема есть. Проблема в большей степени связана с тем, что библиотеки SQLite создают НОВЫЙ контейнер для размещения результатов каждого нового запроса, а не используют один и тот же контейнер снова и снова. И хотя я могу закрыть курсор, скорость очистки GC медленнее, чем скорость, с которой создаются новые контейнеры, что приводит к увеличению объема памяти.
Показать ещё 3 комментария
Теги:
memory-leaks
memory
cursor

3 ответа

80

Чаще всего причиной этой ошибки являются не закрытые курсоры. Убедитесь, что вы закрыли все курсоры после их использования (даже в случае ошибки).

Cursor cursor = null;
try {
    cursor = db.query(...
    // do some work with the cursor here.
} finally {
    // this gets called even if there is an exception somewhere above
    if(cursor != null)
        cursor.close();
}
  • 0
    Вы можете просто использовать этот пример, чтобы избежать проверки на ноль и ноль.
  • 3
    Как и указатели на открытые файлы - ВСЕГДА обрабатывайте закрытие в секции finally, чтобы гарантировать, что ваш код существует чисто.
58

Если вам нужно прорыть значительное количество кода SQL, вы можете ускорить отладку, поместив следующий фрагмент кода в свой MainActivity, чтобы включить StrictMode. Если обнаружены пропущенные объекты базы данных, ваше приложение теперь будет разбиваться с информацией о журнале, точно указывая, где находится ваша утечка. Это помогло мне найти мошенника в считанные минуты.

@Override
protected void onCreate(Bundle savedInstanceState) {
   if (BuildConfig.DEBUG) {     
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
         .detectLeakedSqlLiteObjects()
         .detectLeakedClosableObjects()
         .penaltyLog()
         .penaltyDeath()
         .build());
    }
    super.onCreate(savedInstanceState);
    ...
    ...
  • 6
    Замечательно! Я сделал этот стандарт с if (BuildConfig.DEBUG) {...} внутри блока static {...} моего основного класса.
  • 1
    Лучший и самый эффективный способ отладки такой проблемы, которую я нашел до сих пор, должен быть принятым ответом!
Показать ещё 3 комментария
10

Я только что испытал эту проблему - и предлагаемый ответ не закрывать курсор в то время как действительный, не был тем, как я его исправил. Моя проблема заключалась в закрытии базы данных, когда SQLite пыталась переместить курсор. Я бы открыл базу данных, запросил базу данных, чтобы получить указатель на набор данных, закрыть базу данных и перебрать курсор. Я заметил, что всякий раз, когда я нажимаю определенную запись в этом курсоре, мое приложение рушится с той же ошибкой в ​​OP.

Я предполагаю, что для того, чтобы курсор получал доступ к определенным записям, ему необходимо повторно запросить базу данных, и если она будет закрыта, она будет вызывать эту ошибку. Я исправил его, не закрывая базу данных, пока не выполнил всю необходимую мне работу.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню