Android的Cursor的close方法不调用会不会造成内存泄露

研究内存问题时,突然想到Android的Cursor的Close如果不调用会不会造成内存泄露。
于是翻开Android源码一番跟踪,发现google已经为开发者加了一层保证,即时不调用Close方法,也不会造成内存泄露。
即使如此,开发者最好还是自己调用close进行Cursor的资源释放。
下面看分析流程:

一般我们都是通过context的openOrCreateDatabase方法得到SQLiteDatabase对象进行数据库操作。
WrapContext->openOrCreateDatabase("city.db", Context.MODE_PRIVATE, null)具体实现在ContextImpl中。

第三个参数为CursorFactory对象,并且此对象决定返回的Cursor类型。
如果CursorFactory不传入null,需要调用openOrCreateDatabase的地方实现CursorFactory
在看CursorFactory这个接口,代码在SQLiteDatabase中

    /**
     * Used to allow returning sub-classes of {@link Cursor} when calling query.
     */
    public interface CursorFactory {
        /**
         * See
         * {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}.
         */
        public Cursor newCursor(SQLiteDatabase db,
                SQLiteCursorDriver masterQuery, String editTable,
                SQLiteQuery query);
    }


所以必须要实现newCursor方法。
再看传入null时Android系统的实现,具体实现在SQLiteDirectCursorDriver这个类中

    public Cursor query(CursorFactory factory, String[] selectionArgs) {
        // Compile the query
        SQLiteQuery query = null;
        try {
            mDatabase.lock(mSql);
            mDatabase.closePendingStatements();
            query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
            // Create the cursor
            if (factory == null) {
                mCursor = new SQLiteCursor(this, mEditTable, query);
            } else {
                mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
            }
            mQuery = query;
            query = null;
            return mCursor;
        } finally {
            // Make sure this object is cleaned up if something happens
            if (query != null) query.close();
            mDatabase.unlock();
        }
    }


看红色部分即为系统的处理方式,如果应用程序自己实现CursorFactory就会调用应用程序实现的
newCursor方法。如果应用不实现,那么就会返回 SQLiteCursor这个对象, SQLiteCursor间接实现了
Cursor接口,这个 Cursor对象即应用中通常使用的 Cursor

再看 SQLiteCursor的构造方法
        query.mDatabase.lock(query.mSql);
        try {
            // Setup the list of columns
            int columnCount = mQuery.columnCountLocked();
            mColumns = new String[columnCount];
            // Read in all column names
            for (int i = 0; i < columnCount; i++) {
                String columnName = mQuery.columnNameLocked(i);
                mColumns[i] = columnName;
                if (false) {
                    Log.v("DatabaseWindow", "mColumns[" + i + "] is "
                            + mColumns[i]);
                }
   
                // Make note of the row ID column index for quick access to it
                if ("_id".equals(columnName)) {
                    mRowIdColumnIndex = i;
                }
            }
        } finally {
            query.mDatabase.unlock();
        }

可以看到在构造方法里面会通过 SQLiteQuery去查询数据库数据生成 Cursor数据。
至此带数据Cursor就生成了,并且知道Cursor具体是实现在什么地方。

最后再看一下 SQLiteCursor里的 finalize方法,finalize是java GC机制会调用到此方法,
java中并不建议重写这个方法,但是这里为什么还要重写呢,目的就是释放native的C++资源。
看代码:
    /**
     *  Release the native resources, if they haven't been released yet.
     */
    @Override
    protected void finalize() {
        try {
            // if the cursor hasn't been closed yet, close it first
            if (mWindow != null) {
                if (StrictMode.vmSqliteObjectLeaksEnabled()) {
                    int len = mQuery.mSql.length();
                    StrictMode.onSqliteObjectLeaked(
                        "Finalizing a Cursor that has not been deactivated or closed. " +
                        "database = " + mQuery.mDatabase.getPath() + ", table = " + mEditTable +
                        ", query = " + mQuery.mSql.substring(0, (len > 1000) ? 1000 : len),
                        mStackTrace);
                }
                close();
                SQLiteDebug.notifyActiveCursorFinalized();
            } else {
                if (false) {
                    Log.v(TAG, "Finalizing cursor on database = " + mQuery.mDatabase.getPath() +
                            ", table = " + mEditTable + ", query = " + mQuery.mSql);
                }
            }
        } finally {
            super.finalize();
        }
    }


现在知道为什么很多时候我们写代码一次次的查询又没有调用cursor的close()方法,却没有造成内存泄露了吧,
应该google帮我们做了一层保障,就是当cursor用完了之后,并且已经没有没有引用指向Cursor( 很多时候
习惯都是在查询完之后,在某个方法体中对Cursor进行遍历并生成list),这个这个Cursor就再也没有引用指向他,
那么下一次GC的时候就会调用到finalize方法,这个时候就会执行close了。

但是万一什么时候自己实现CursorFactory并实现Cursor的时候,又没有覆盖finalize方法,同时又没有在程序中主动调用
close方法进行native资源释放,就会造成内存泄露。

写代码的时候Cursor使用完,最好主动调用close方法,因为cursor不关闭虽然不会造成内存泄漏,
但是会耗尽IO资源(虚拟机并没有执行GC,又有新的cursor操作,导致cursor的finalize方法并没有执行,
就申请新的IO资源,会直接在native crash),。

















你可能感兴趣的:(Android)