研究内存问题时,突然想到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),。