chopchop
chopchop

Reputation: 1933

Out of Memory when allocating cursors

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.

Upvotes: 18

Views: 23601

Answers (2)

Graham Borland
Graham Borland

Reputation: 60681

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();
}

Upvotes: 27

Shrikant Ballal
Shrikant Ballal

Reputation: 7087

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.

Upvotes: 10

Related Questions