Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 202
chopchop Avatar asked Sep 25 '12 06:09

chopchop


2 Answers

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();
}
like image 74
Graham Borland Avatar answered Oct 23 '22 19:10

Graham Borland


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.

like image 36
Shrikant Ballal Avatar answered Oct 23 '22 18:10

Shrikant Ballal