1

I have two fragments where inside both of these fragments, it will execute codes that utilize sqlbrite. While the app won't crash in normal usage, but the problem is, if I load these 2 fragments back-and-forth multiple times in a short period, the app will crash with this error.

E/CursorWindow: Could not allocate CursorWindow '/data/data/com.imincode.meniti/databases/meniti' of size 2097152 due to error -12.
E/CursorWindow: Could not allocate CursorWindow '/data/data/com.imincode.meniti/databases/meniti' of size 2097152 due to error -12.
E/CursorWindow: Could not allocate CursorWindow '/data/data/com.imincode.meniti/databases/meniti' of size 2097152 due to error -12.
W/libc: pthread_create failed: couldn't allocate 1064960-byte stack: Out of memory
W/libc: pthread_create failed: couldn't allocate 1064960-byte stack: Out of memory
W/System.err: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. 
W/System.err: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. 
W/System.err: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. 
E/CursorWindow: Could not allocate CursorWindow '/data/data/com.imincode.meniti/databases/meniti' of size 2097152 due to error -12.
W/System.err: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. 
E/CursorWindow: Could not allocate CursorWindow '/data/data/com.imincode.meniti/databases/meniti' of size 2097152 due to error -12.
W/System.err: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. 
E/art: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"
E/art: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"
W/System.err:     at    at android.database.CursorWindow.<init>(CursorWindow.java:108)
W/System.err:     at    at android.database.CursorWindow.<init>(CursorWindow.java:108)
W/System.err:     at    at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
W/System.err:     at android.database.CursorWindow.<init>(CursorWindow.java:108)
W/System.err:     at    at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
W/System.err:     at android.database.CursorWindow.<init>(CursorWindow.java:108)
W/System.err:     at android.database.CursorWindow.<init>(CursorWindow.java:108)
W/System.err:     at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
W/System.err:     at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
W/System.err:     at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
W/System.err:     at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
W/System.err:     at com.imincode.meniti.db.DbHelper$6.call(DbHelper.java:438)
W/System.err:     at com.imincode.meniti.db.DbHelper$6.call(DbHelper.java:431)
W/System.err:     at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
W/System.err:     at rx.observers.Subscribers$5.onNext(Subscribers.java:235)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.emit(OperatorOnBackpressureLatest.java:165)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.onNext(OperatorOnBackpressureLatest.java:131)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestSubscriber.onNext(OperatorOnBackpressureLatest.java:211)
W/System.err:     at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:224)
W/System.err:     at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
W/System.err: android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
W/System.err:     at    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
W/System.err:     at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
W/System.err:     at com.imincode.meniti.db.DbHelper$4.call(DbHelper.java:368)
W/System.err:     at com.imincode.meniti.db.DbHelper$4.call(DbHelper.java:361)
W/System.err:     at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
W/System.err:     at rx.observers.Subscribers$5.onNext(Subscribers.java:235)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.emit(OperatorOnBackpressureLatest.java:165)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.onNext(OperatorOnBackpressureLatest.java:131)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestSubscriber.onNext(OperatorOnBackpressureLatest.java:211)
W/System.err:     at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:224)
W/System.err:     at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
W/System.err:     at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
W/System.err:     at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
W/System.err:     at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
W/System.err:     at com.imincode.meniti.db.DbHelper$6.call(DbHelper.java:438)
W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
W/System.err:     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
W/System.err:     at com.imincode.meniti.db.DbHelper$6.call(DbHelper.java:431)
W/System.err:     at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
W/System.err:     at rx.observers.Subscribers$5.onNext(Subscribers.java:235)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.emit(OperatorOnBackpressureLatest.java:165)
W/System.err: android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
W/System.err:     at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
W/System.err:     at com.imincode.meniti.db.DbHelper$4.call(DbHelper.java:368)
W/System.err:     at com.imincode.meniti.db.DbHelper$4.call(DbHelper.java:361)
W/System.err:     at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
W/System.err:     at rx.observers.Subscribers$5.onNext(Subscribers.java:235)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.emit(OperatorOnBackpressureLatest.java:165)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.onNext(OperatorOnBackpressureLatest.java:131)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestSubscriber.onNext(OperatorOnBackpressureLatest.java:211)
W/System.err:     at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:224)
W/System.err:     at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
W/System.err:     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W/System.err:     at java.lang.Thread.run(Thread.java:818)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.onNext(OperatorOnBackpressureLatest.java:131)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestSubscriber.onNext(OperatorOnBackpressureLatest.java:211)
W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W/System.err:     at java.lang.Thread.run(Thread.java:818)com.imincode.meniti.db.DbHelper$4.call(DbHelper.java:368)
W/System.err:     at com.imincode.meniti.db.DbHelper$4.call(DbHelper.java:361)
W/System.err:     at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
W/System.err:     at rx.observers.Subscribers$5.onNext(Subscribers.java:235)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.emit(OperatorOnBackpressureLatest.java:165)
W/System.err: rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:224)
W/System.err:     at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
W/System.err:     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W/System.err:     at java.lang.Thread.run(Thread.java:818)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestEmitter.onNext(OperatorOnBackpressureLatest.java:131)
W/System.err:     at rx.internal.operators.OperatorOnBackpressureLatest$LatestSubscriber.onNext(OperatorOnBackpressureLatest.java:211)
W/System.err:     at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:224)
W/System.err:     at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
W/System.err: rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
W/System.err:     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W/System.err:     at java.lang.Thread.run(Thread.java:818)
W/System.err:     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W/System.err:     at java.lang.Thread.run(Thread.java:818)
W/libc: pthread_create failed: couldn't allocate 1064960-byte stack: Out of memory
E/art: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"
W/libc: pthread_create failed: couldn't allocate 1064960-byte stack: Out of memory
E/art: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"

One of the fragment has about 50 Observable which are then combined using Observable.combineLatest. Here's the code snippet for the fragment (the other fragment have a similar code). Below is basically the simplified version of my code.

public class MenuSummary extends Fragment{

private DbHelper dbHelper;
private Observable<?> incomeFromParentJar, getIncomeNecessities, getIncomeSavings, getIncomeEntertainment, getIncomeInvestment, getIncomeEducation, getIncomeCharity;
private Observable<?> getExpensesNecessities, getExpensesSavings, getExpensesEntertainment, getExpensesInvestment, getExpensesEducation, getExpensesCharity;
private Observable<?> getTotalUnpaidMonthlyRecurringExpensesNecessities, getTotalUnpaidMonthlyRecurringExpensesSavings;
private Observable imin;
private Subscription s;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    dbHelper = new DbHelper(getContext());
}

@Override
public void onPause(){
    super.onPause();
    dbHelper.close();
    s.unsubscribe();
}

@Override
public void onResume(){
    super.onResume();
    displaySummary(selectedYear,selectedMonth);
}

@Override
public void onStop() {
    super.onStop();
}

@Override
public void onActivityCreated(Bundle savedInstanceState){
    super.onActivityCreated(savedInstanceState);
}

private void displaySummary(final int selectedYear, final int selectedMonth){

    incomeFromParentJar = dbHelper.getParentJarIncomeUpTo(selectedYear,selectedMonth);
    getIncomeNecessities = dbHelper.getIndividualJarIncomeUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_NECESSITIES);
    getIncomeSavings = dbHelper.getIndividualJarIncomeUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_SAVINGS);
    getIncomeEntertainment = dbHelper.getIndividualJarIncomeUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_ENTERTAINMENT);
    getIncomeInvestment = dbHelper.getIndividualJarIncomeUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_INVESTMENT);
    getIncomeEducation = dbHelper.getIndividualJarIncomeUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_EDUCATION);
    getIncomeCharity = dbHelper.getIndividualJarIncomeUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_CHARITY);

    getExpensesNecessities = dbHelper.getJarExpensesUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_NECESSITIES);
    getExpensesSavings = dbHelper.getJarExpensesUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_SAVINGS);
    getExpensesEntertainment = dbHelper.getJarExpensesUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_ENTERTAINMENT);
    getExpensesInvestment = dbHelper.getJarExpensesUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_INVESTMENT);
    getExpensesEducation = dbHelper.getJarExpensesUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_EDUCATION);
    getExpensesCharity = dbHelper.getJarExpensesUpTo(selectedYear,selectedMonth,MySQLiteHelper.JAR_CHARITY);

    List<Observable<?>> myObservables = Arrays.asList(incomeFromParentJar,getIncomeNecessities,getIncomeSavings,getIncomeEntertainment, getIncomeInvestment,getIncomeEducation,getIncomeCharity,getExpensesNecessities,getExpensesSavings,getExpensesEntertainment,getExpensesInvestment, getExpensesEducation,getExpensesCharity);
    imin = Observable.combineLatest(myObservables, new FuncN<List<BigDecimal>>() {
        @Override
        public List<BigDecimal> call(Object... args) {
            BigDecimal incomeTotal, incomeNecessities, incomeSavings, incomeEntertainment;
            BigDecimal incomeInvestment, incomeEducation, incomeCharity;
            List<BigDecimal> incomeFromParentJar = (List<BigDecimal>) args[0];
            List<BigDecimal> expensesAndBalance = new ArrayList<BigDecimal>();

            incomeTotal = incomeFromParentJar.get(0);
            incomeNecessities = incomeFromParentJar.get(1);
            incomeSavings = incomeFromParentJar.get(2);
            incomeEntertainment = incomeFromParentJar.get(3);
            incomeInvestment = incomeFromParentJar.get(4);
            incomeEducation = incomeFromParentJar.get(5);
            incomeCharity = incomeFromParentJar.get(6);

            incomeNecessities = incomeNecessities.add((BigDecimal) args[1]);
            .....
            .....
            .....
        }
    }
).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
s = imin.subscribe(new Action1<List<BigDecimal>>() {
@Override
public void call(List<BigDecimal> expensesAndBalance) {
    //do plenty of view.settext here
}});
}

And here's how the DbHelper class looks like. Take note that I only display the code for one of the called function.. the other function is pretty much the same (except for the query of course)..

Can anyone pinpoint where did I went wrong?

   public class DbHelper {

        private MySQLiteHelper mySQLiteHelper;
        SqlBrite sqlBrite = new SqlBrite.Builder().build();
        BriteDatabase briteDb;
        Subscription subscription;

        private static DbHelper instance;
        public DbHelper(Context context) {
            mySQLiteHelper = new MySQLiteHelper(context);
            sqlBrite = new SqlBrite.Builder().build();
        }

        public Observable<BigDecimal> getIndividualJarIncomeUpTo(final int endYear, final int endMonth, final int moneyJar){
            briteDb = sqlBrite.wrapDatabaseHelper(mySQLiteHelper,Schedulers.io());
        final String[] args = new String[]{moneyJar + "", endYear + "", endMonth + "", endYear + "", };
        Observable<BigDecimal> myObservable;
        myObservable = briteDb.createQuery(MySQLiteHelper.TABLE_INCOME, "SELECT total FROM " + MySQLiteHelper.TABLE_INCOME +
                " WHERE moneyJar = ? AND ((year = ? AND month <= ?) OR (year < ?)) and isDeleted = 0", args)
                .map(new Func1<SqlBrite.Query, BigDecimal>() {
                    @Override
                    public BigDecimal call(SqlBrite.Query query) {
                        Cursor cursor = query.run();
                        BigDecimal income, incomeTotal = new BigDecimal(0);
                        if (cursor != null) {
                            try {
                                if (cursor.getCount() > 0 && cursor.moveToFirst()) {
                                    do {
                                        income = new BigDecimal(cursor.getString(cursor.getColumnIndex("total")));
                                        incomeTotal = incomeTotal.add(income);
                                    } while (cursor.moveToNext());
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                cursor.close();
                            }
                            if (moneyJar == 1) {
                                Log.v(FILE_NAME, "imini " + endYear + " " + endMonth + " incomeTotal: " + incomeTotal);
                            }
                        }
                        return incomeTotal;
                    }
                });
        return myObservable;
    }

    public void close(){
        if (briteDb != null) {
            briteDb.close();
        }
    }
}
imin
  • 4,504
  • 13
  • 56
  • 103
  • seems like a general android cursor/memory problem, I would suggest to ensure all cursors are closed correctly, although code seems fine (maybe unsubscribing not handling cursor closing right?), you should try to track open cursors using StrictMode. besides that it's might be a general out of memory problem, try to tack the process size to see if it's related. – yosriz Mar 26 '17 at 07:41
  • @yosriz. When I enabled strict mode, the app will crash when orientation changes with the message that some open cursors are not closed. And as you can see, I've already added cursor.close() in finally. I'm not sure I know any other method to close the cursors. – imin Mar 26 '17 at 16:34
  • Just to be sure the cursor.close() is reached, I run the app in debug and put brakepoints in all cursor.close(), and all brakepoints are reached. – imin Mar 26 '17 at 16:35
  • Or I should open another question about somehow cursor.close is not always being called? – imin Mar 27 '17 at 05:48
  • Hmmm I tried to reduce the number of observables that's returned and combined to only 5 (from about more than 50) and understandably this out of memory doesn't occur anymore. – imin Mar 27 '17 at 15:27
  • did you solve the unclosed cursors? did you profile your app memory? it might be that your simply loading a lot of data and when you repeat the reloading process it just go out of memory? – yosriz Mar 27 '17 at 18:32
  • @yosriz yes it seems this is the case. I cut down my observables to only 12 instead of 60 (combined plenty of query too) and this problem doesn't occur anymore. Any idea on what should I write as the answer to this problem? – imin Mar 31 '17 at 17:44

1 Answers1

1

I restructured my codes and sql query and lessen the number of observables to only 12 instead of the original 60, and the problem is gone. So basically this issue was caused by bad design on my part.

imin
  • 4,504
  • 13
  • 56
  • 103