安卓_数据库泄露
2014-05-01 17:57
232 查看
今天遇到系统提示数据库泄露了
不过找了好久也没找到。最后搜索下方案。
添加多下面这段红色的代码。就可以帮助你及时的找到问题。
import android.os.StrictMode;
public class TestActivity extends Activity {
private static final boolean DEVELOPER_MODE = true;
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setVMPolicy(new StrictMode.VMPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
}
从 GINGERBREAD(安卓2.3版本) 开始 Android 就提供了 StrictMode 工具协助开发人员检查是否不小心地做了一些不该有的操作。
使用方法是在 Activity 里面设置 StrictMode,上面的例子是打开了检查泄漏的 SQLite 对象以及 Closeable 对象
(普通 Cursor/FileInputStream 等)的功能,发现有违规情况则记录 log 并使程序强行退出。
下面是转载的原文
简介:
本文介绍如何在 Android 检测 Cursor 泄漏的原理以及使用方法,还指出几种常见的出错示例。有一些泄漏在代码中难以察觉,但程序长时间运行后必然会出现异常。同时该方法同样适合于其他需要检测资源泄露的情况。
最近发现某蔬菜手机连接程序在查询媒体存储(MediaProvider)数据库时出现严重 Cursor 泄漏现象,运行一段时间后会导致系统中所有使用到该数据库的程序无法使用。另外在工作中也常发现有些应用有 Cursor 泄漏现象,由于需要长时间运行才会出现异常,所以有的此类 bug 很长时间都没被发现。
但是一旦 Cursor 泄漏累计到一定数目(通常为数百个)必然会出现无法查询数据库的情况,只有等数据库服务所在进程死掉重启才能恢复正常。通常的出错信息如下,指出某 pid 的程序打开了 866 个 Cursor 没有关闭,导致了 exception:
在 Cursor 对象被 JVM 回收运行到 finalize() 方法的时候,检测 close() 方法有没有被调用,此办法在 ContentResolver 里面也得到应用。简化后的示例代码如下:
然后查询的时候,把 TestCursor 作为查询结果返回给 APP:
该方法同样适合于所有需要检测显式释放资源方法没有被调用的情形,是一种通用方法。但在 finalize() 方法里检测需要注意
优点:准确。因为该资源在 Cursor 对象被回收时仍没被释放,肯定是发生了资源泄露。
缺点:依赖于 finalize() 方法,也就依赖于 JVM 的垃圾回收策略。例如某 APP 现在有 10 个 Cursor 对象泄露,并且这 10 个对象已经不再被任何引用指向处于可回收状态,但是 JVM 可能并不会马上回收(时间不可预测),如果你现在检查不能够发现问题。另外,在某些情况下就算对象被回收 finalize() 可能也不会执行,也就是不能保证检测出所有问题。关于 finalize() 更多信息可以参考《Effective Java 2nd Edition》的 Item 7: Avoid Finalizers
从 GINGERBREAD 开始 Android 就提供了 StrictMode 工具协助开发人员检查是否不小心地做了一些不该有的操作。使用方法是在 Activity 里面设置 StrictMode,下面的例子是打开了检查泄漏的 SQLite 对象以及 Closeable 对象(普通 Cursor/FileInputStream 等)的功能,发现有违规情况则记录 log 并使程序强行退出。
如果是通过 ContentProvider 提供数据库数据,在 ContentResolver 里面已有 CloseGuard 类实行类似检测,但需要自行打开(上例也是打开 CloseGuard):
更值得推荐的办法是按照本文第一节中的检测原理,在 ContentResolver 内部类 CursorWrapperInner 里面加入。其他需要检测类似于资源泄漏的,同样可以使用该检测原理。
忘记调用 close() 这种低级错误没什么好说的,这种应该也占不小的比例。下面说说不太明显的例子。
有时候粗心会犯这种错误,在 close() 调用之前就 return 了,特别是函数比较大逻辑比较复杂时更容易犯错。这种情况可以通过把 close() 放在 finally 代码块解决
假设类里面有一个在类全局有效的成员变量,在方法 A 获取了查询结果,后面在其他地方又获取了一次查询结果,那么第二次查询的时候就应该先把前面一个 Cursor 对象关闭。
注意:曾经遇到过有人对 mCursor 感到疑惑,明明是同一个变量为什么还需要先关闭?首先 mCursor 是一个 Cursor 对象的引用,在 methodA 时 mCursor 指向了 query() 返回的一个 Cursor 对象 1;在 methodB() 时它又指向了返回的另外一个 Cursor 对象 2。在指向 Cursor 对象 2 之前必须先关闭 Cursor 对象 1,否则就出现了 Cursor 对象 1 在 finalize() 之前没有调用 close() 的情况。
打开和关闭 Cursor 之间的代码出现 exception,导致没有跑到关闭的地方:
这种情况应该把 close() 放到 finally 代码块里面:
在 finalize() 里面检测是可行的,且基本可以满足需要。针对 finalize() 执行时间不确定以及可能不执行的问题,可以通过记录目前打开没关闭的 Cursor 数量来部分解决,超过一定数目发出警告,两种手段相结合。
还有没有其他检测办法呢?有,在 Cursor 构造方法以及 close() 方法添加 log,运行一段时间后检查 log 看哪个地方没有关闭。简化代码如下:
检查时看某个 hashCode() 的 Cursor 有没有调用过 close() 方法,没有的话说明资源有泄露。这种方法优点是同样准确,且更可靠。缺点是需要检查大量 log,且打开/关闭的地方可能相距较远,如果不写个小脚本分析人工看的话会比较痛苦;另外必须 APP 完全退出后才能检查,因为后台运行时某些 Cursor 还在正常使用。
转载请注明出处:http://www.cnblogs.com/imouto/archive/2013/01/14/how-to-detect-leaked-cursor.html
本文外部镜像:http://oteku.blogspot.com/2013/01/how-to-detect-android-cursor-leak-cn.html
不过找了好久也没找到。最后搜索下方案。
添加多下面这段红色的代码。就可以帮助你及时的找到问题。
import android.os.StrictMode;
public class TestActivity extends Activity {
private static final boolean DEVELOPER_MODE = true;
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setVMPolicy(new StrictMode.VMPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
}
对于 APP 开发人员
从 GINGERBREAD(安卓2.3版本) 开始 Android 就提供了 StrictMode 工具协助开发人员检查是否不小心地做了一些不该有的操作。使用方法是在 Activity 里面设置 StrictMode,上面的例子是打开了检查泄漏的 SQLite 对象以及 Closeable 对象
(普通 Cursor/FileInputStream 等)的功能,发现有违规情况则记录 log 并使程序强行退出。
下面是转载的原文
简介:
本文介绍如何在 Android 检测 Cursor 泄漏的原理以及使用方法,还指出几种常见的出错示例。有一些泄漏在代码中难以察觉,但程序长时间运行后必然会出现异常。同时该方法同样适合于其他需要检测资源泄露的情况。
最近发现某蔬菜手机连接程序在查询媒体存储(MediaProvider)数据库时出现严重 Cursor 泄漏现象,运行一段时间后会导致系统中所有使用到该数据库的程序无法使用。另外在工作中也常发现有些应用有 Cursor 泄漏现象,由于需要长时间运行才会出现异常,所以有的此类 bug 很长时间都没被发现。
但是一旦 Cursor 泄漏累计到一定数目(通常为数百个)必然会出现无法查询数据库的情况,只有等数据库服务所在进程死掉重启才能恢复正常。通常的出错信息如下,指出某 pid 的程序打开了 866 个 Cursor 没有关闭,导致了 exception:
3634 3644 E JavaBinder: *** Uncaught remote exception! (Exceptions are not yet supported across processes.) 3634 3644 E JavaBinder: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=866 (# cursors opened by pid 1565=866) 3634 3644 E JavaBinder: at android.database.CursorWindow.(CursorWindow.java:104) 3634 3644 E JavaBinder: at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198) 3634 3644 E JavaBinder: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:147) 3634 3644 E JavaBinder: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:141) 3634 3644 E JavaBinder: at android.database.CursorToBulkCursorAdaptor.getBulkCursorDescriptor(CursorToBulkCursorAdaptor.java:143) 3634 3644 E JavaBinder: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:118) 3634 3644 E JavaBinder: at android.os.Binder.execTransact(Binder.java:367) 3634 3644 E JavaBinder: at dalvik.system.NativeStart.run(Native Method) |
1. Cursor 检测原理
在 Cursor 对象被 JVM 回收运行到 finalize() 方法的时候,检测 close() 方法有没有被调用,此办法在 ContentResolver 里面也得到应用。简化后的示例代码如下:1 import android.database.Cursor; 2 import android.database.CursorWrapper; 3 import android.util.Log; 4 5 public class TestCursor extends CursorWrapper { 6 private static final String TAG = "TestCursor"; 7 private boolean mIsClosed = false; 8 private Throwable mTrace; 9 10 public TestCursor(Cursor c) { 11 super(c); 12 mTrace = new Throwable("Explicit termination method 'close()' not called"); 13 } 14 15 @Override 16 public void close() { 17 mIsClosed = true; 18 } 19 20 @Override 21 public void finalize() throws Throwable { 22 try { 23 if (mIsClosed != true) { 24 Log.e(TAG, "Cursor leaks", mTrace); 25 } 26 } finally { 27 super.finalize(); 28 } 29 } 30 }
然后查询的时候,把 TestCursor 作为查询结果返回给 APP:
1 return new TestCursor(cursor); // cursor 是普通查询得到的结果,例如从 ContentProvider.query()
该方法同样适合于所有需要检测显式释放资源方法没有被调用的情形,是一种通用方法。但在 finalize() 方法里检测需要注意
优点:准确。因为该资源在 Cursor 对象被回收时仍没被释放,肯定是发生了资源泄露。
缺点:依赖于 finalize() 方法,也就依赖于 JVM 的垃圾回收策略。例如某 APP 现在有 10 个 Cursor 对象泄露,并且这 10 个对象已经不再被任何引用指向处于可回收状态,但是 JVM 可能并不会马上回收(时间不可预测),如果你现在检查不能够发现问题。另外,在某些情况下就算对象被回收 finalize() 可能也不会执行,也就是不能保证检测出所有问题。关于 finalize() 更多信息可以参考《Effective Java 2nd Edition》的 Item 7: Avoid Finalizers
2. 使用方法
对于 APP 开发人员
从 GINGERBREAD 开始 Android 就提供了 StrictMode 工具协助开发人员检查是否不小心地做了一些不该有的操作。使用方法是在 Activity 里面设置 StrictMode,下面的例子是打开了检查泄漏的 SQLite 对象以及 Closeable 对象(普通 Cursor/FileInputStream 等)的功能,发现有违规情况则记录 log 并使程序强行退出。1 import android.os.StrictMode; 2 3 public class TestActivity extends Activity { 4 private static final boolean DEVELOPER_MODE = true; 5 public void onCreate() { 6 if (DEVELOPER_MODE) { 7 StrictMode.setVMPolicy(new StrictMode.VMPolicy.Builder() 8 .detectLeakedSqlLiteObjects() 9 .detectLeakedClosableObjects() 10 .penaltyLog() 11 .penaltyDeath() 12 .build()); 13 } 14 super.onCreate(); 15 } 16 }
对于 framework 开发人员
如果是通过 ContentProvider 提供数据库数据,在 ContentResolver 里面已有 CloseGuard 类实行类似检测,但需要自行打开(上例也是打开 CloseGuard):1 CloseGuard.setEnabled(true);
更值得推荐的办法是按照本文第一节中的检测原理,在 ContentResolver 内部类 CursorWrapperInner 里面加入。其他需要检测类似于资源泄漏的,同样可以使用该检测原理。
3. 容易出错的地方
忘记调用 close() 这种低级错误没什么好说的,这种应该也占不小的比例。下面说说不太明显的例子。
提前返回
有时候粗心会犯这种错误,在 close() 调用之前就 return 了,特别是函数比较大逻辑比较复杂时更容易犯错。这种情况可以通过把 close() 放在 finally 代码块解决1 private void method() { 2 Cursor cursor = query(); // 假设 query() 是一个查询数据库返回 Cursor 结果的函数 3 if (flag == false) { // !!提前返回 4 return; 5 } 6 cursor.close(); 7 }
类的成员变量
假设类里面有一个在类全局有效的成员变量,在方法 A 获取了查询结果,后面在其他地方又获取了一次查询结果,那么第二次查询的时候就应该先把前面一个 Cursor 对象关闭。1 public class TestCursor { 2 private Cursor mCursor; 3 4 private void methodA() { 5 mCursor = query(); 6 } 7 8 private void methodB() { 9 // !!必须先关闭上一个 cursor 对象 10 mCursor = query(); 11 } 12 }
注意:曾经遇到过有人对 mCursor 感到疑惑,明明是同一个变量为什么还需要先关闭?首先 mCursor 是一个 Cursor 对象的引用,在 methodA 时 mCursor 指向了 query() 返回的一个 Cursor 对象 1;在 methodB() 时它又指向了返回的另外一个 Cursor 对象 2。在指向 Cursor 对象 2 之前必须先关闭 Cursor 对象 1,否则就出现了 Cursor 对象 1 在 finalize() 之前没有调用 close() 的情况。
异常处理
打开和关闭 Cursor 之间的代码出现 exception,导致没有跑到关闭的地方:1 try { 2 Cursor cursor = query(); 3 // 中间省略某些出现异常的代码 4 cursor.close(); 5 } catch (Exception e) { 6 // !!出现异常没跑到 cursor.close() 7 }
这种情况应该把 close() 放到 finally 代码块里面:
1 Cursor cursor = null; 2 try { 3 cursor = query(); 4 // 中间省略某些出现异常的代码 5 } catch (Exception e) { 6 // 出现异常 7 } finally { 8 if (cursor != null) 9 cursor.close(); 10 }
4. 总结思考
在 finalize() 里面检测是可行的,且基本可以满足需要。针对 finalize() 执行时间不确定以及可能不执行的问题,可以通过记录目前打开没关闭的 Cursor 数量来部分解决,超过一定数目发出警告,两种手段相结合。还有没有其他检测办法呢?有,在 Cursor 构造方法以及 close() 方法添加 log,运行一段时间后检查 log 看哪个地方没有关闭。简化代码如下:
1 import android.database.Cursor; 2 import android.database.CursorWrapper; 3 import android.util.Log; 4 5 public class TestCursor extends CursorWrapper { 6 private static final String TAG = "TestCursor"; 7 private Throwable mTrace; 8 9 public TestCursor(Cursor c) { 10 super(c); 11 mTrace = new Throwable("cusor opened here"); 12 Log.d(TAG, "Cursor " + this.hashCode() + " opened, stacktrace is: ", mTrace); 13 } 14 15 @Override 16 public void close() { 17 mIsClosed = true; 18 Log.d(TAG, "Cursor " + this.hashCode() + " closed."); 19 } 20 }
检查时看某个 hashCode() 的 Cursor 有没有调用过 close() 方法,没有的话说明资源有泄露。这种方法优点是同样准确,且更可靠。缺点是需要检查大量 log,且打开/关闭的地方可能相距较远,如果不写个小脚本分析人工看的话会比较痛苦;另外必须 APP 完全退出后才能检查,因为后台运行时某些 Cursor 还在正常使用。
转载请注明出处:http://www.cnblogs.com/imouto/archive/2013/01/14/how-to-detect-leaked-cursor.html
本文外部镜像:http://oteku.blogspot.com/2013/01/how-to-detect-android-cursor-leak-cn.html
相关文章推荐
- (安卓) 三级缓存具体逻辑(内存,磁盘{数据库},网络)
- 网络安全法通过30天后京东数据库泄露,这是巧合?
- 安卓微信数据库解密
- 关于安卓通过webservice访问数据库问题
- 利用cmd窗口查询安卓手机数据库文件
- java关于安卓,苹果输入表情数据库处理
- 安卓微信数据库解密
- 安卓数据库框架LitePal(二):基本用法
- 安卓开发使用Sqllite数据库动态存储数据
- 360回应B站数据库疑似泄露事件
- 安卓原生操作数据库时,使用ContentValues类
- 安卓Android通过字段映射创建sqlite数据库,操作数据库,创建表,增、删、改查,通用方法,轻松编程(by寒冬)
- 关于安卓版的eclipse连接数据库并誓言增删改查
- 安卓服务端开发(1) 安卓结合PHP实现连接数据库验证登录功能(附全部代码)
- 安卓基础:数据库的使用
- 未root的安卓真机使用sqlite3查看data目录下的数据库
- 安卓,从数据库抽取的数据,检索<br></n>等,并实现换行
- Btrace 定位数据库连接泄露(初试)
- 你经常访问那些安卓网站?附 几百个技巧大全网址,电纸书网站,查开房隐私泄露网址等
- 安卓小白开发之学习安卓的点点滴滴—数据库(2)