Out of Memory when allocating cursors
Asked Answered
G

2

18

I have a memory problem that I can't figure out. I have one class that does all my database retrieving work. The error I have is the following:

android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=733 (# cursors opened by this proc=733)

The memory allocation error occurs when I do this:

mDatabaseInterface.getGraphForLevel(level);

I know it's a leak because I call this method every 2.5 seconds roughly, and the 5 or 6 first calls go through easily. Now here are the methods in my DatabaseInterface class:

public Graph getGraphForLevel(Level level) {

    //get the nodes
    ArrayList<Node> nodes = new ArrayList<Node>(Arrays.asList(this.getNodesWithLevel(level)));
    //get the edges
    ArrayList<Edge> edges = new ArrayList<Edge>(Arrays.asList(this.getEdgesWithNodes(nodes)));

    return new Graph(nodes, edges);
}

public Node[] getNodesWithLevel(Level level) {

    List<Node> l = new ArrayList<Node>();

    Cursor cursor = mDatabase.query("nodes", null, 
            "level = " + wrapSql(String.valueOf(level.getId())), null, null, null, null);

    while (cursor.moveToNext()) {
        l.add(parseNodeFromCursor(cursor));
    }

    cursor.close();

    return l.toArray(new Node[l.size()]);       
}

private Node parseNodeFromCursor(Cursor cursor) {

    Level l = getLevelWithId(cursor.getInt(2));

    return new Node(cursor.getInt(0), cursor.getString(1), l, 
            cursor.getInt(4), cursor.getInt(5));
}

I have a lot of methods that call each other but I know it's not a recursion problem because this class works in another app. My main question is why doesn't cursor.close() liberate the cursor? If I do something like:

cursor = mDatabase.query(...);
cursor.moveToNext();
Node node = new Node(cursor.getInt());
cursor.close();

Is the cursor retained in that case?

Thanks in advance.

Gabbey answered 25/9, 2012 at 6:36 Comment(2)
Just to get an idea, how big are the tables for nodes and edges ?Slowly
They're very small for now, 20 rows and 10 columns at mostGabbey
G
27

The call to cursor.close() should be in a finally block in case an exception is thrown while you're iterating over it.

Cursor cursor = mDatabase.query("nodes", null, 
        "level = " + wrapSql(String.valueOf(level.getId())), null, null, null, null);
try {
    while (cursor.moveToNext()) {
        l.add(parseNodeFromCursor(cursor));
    }
} finally {
    cursor.close();
}
Groomsman answered 25/9, 2012 at 9:20 Comment(0)
F
10

One of the reasons for occurring Out of Memory error is you are not closing your cursor.

As I can see, you are calling cursor.close(), but is it the right place where you should call this method or check if you should close it on some other place.

EDIT:

If your activity is managing your Cursor, you may consider stop managing it and closing everything in the onPause method, and in onResume open everything up and fillData once again.

Friede answered 25/9, 2012 at 6:40 Comment(6)
mmm thanks I thoroughly checked my code and thought I fixed it. But it came back after 10 minutes of running (a lot better than 10 seconds!). But now instead of 733 or so cursors it crashes with only 4 cursors opened. android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=4 (# cursors opened by this proc=4)Gabbey
Okay. So now, if you are using a global object of cursor, first try clearing the contents of cursor and then fill up the data in it again, so that, the contents of cursor will not exceed the threshold.Friede
I'm not using a global object in every method (and there's a lot) I allocate a cursor then close it before exiting the method. Should I use a global object? also, I'm not sure what you mean by clearing the contentGabbey
Yes, try to use a global context, I think that will definitely solve the problem. (Forget about the clearing contents, I searched about it, its not possible)Friede
Actually I can't do that because I have a lot of nested queries so I need to keep a ref on each cursor, any other ideas? What's best practice for nested queries?Gabbey
let us continue this discussion in chatGabbey

© 2022 - 2024 — McMap. All rights reserved.