Android数据库Cursor异常原因和Fd泄露分析
前言
手机应用和Android终端机应用不同的地方是:Android终端机应用长时间运行,必须保证长时间(几周到几个月不等)稳定运行,不崩溃,而手机应用经常进出,很难发现一些问题。线下监控内存泄漏可以使用LeakCanary、Android Studio 的 Android Profiler、eclispe的MAT这些常规手段,但是不支持线上监控。线上监控应用,可以使用腾讯Bugly、友盟。但是有了线上监控就完事了,不一定,有时候出现一些奇怪的问题,明明看似代码规范,却还是crash了。Android终端机应用crash回到桌面(通过守护进程重新拉起应用),这是非常影响用户体验的。下面举例子分析一个常见的线上异常(数据库用的是Room)
出现异常 CursorWindowAllocationException
CursorWindowAllocationException Cursor window allocation of 2048 kb failed.
可能原因1:
sqlite query完成之后cursor没有关闭。
解决方法:
在finally中关闭cursor
Cursor cursor = null;
try {} finally {if (cursor != null) {cursor.close();}
}
可能原因2
Cursor空间耗尽,比如
线程A执行到1处创建了一个Cursor,然后dosomething比较耗时…,线程B又来查询数据库,因此到1处又创建一个Cursor,此时如果AB执行完,就会关闭锁,看起来没有问题,但由于是同一个对象,所以AB关闭的cursor都是B创建的,因此
A创建的Cursor就没有关闭!
解决方法同上
进一步分析异常
在你的应用中,Cursor正常关闭,还是时不时报CursorWindowAllocationException,这是你就要注意应用的句柄泄露了(Fd泄露),文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
在Linux中一个文件、一个串口、一个socket、一个线程都可以是一个文件,而一个文件会占用一个句柄,linux中一个进程默认的句柄最大数值是1024,当超过这个数值,linux就会对当前的进程进行kill,而kill的对象可以是任意对象,所以会造成各种异常原因的崩溃。
Linux中, 每一个进程在内核中,都对应有一个“打开文件”数组,存放指向文件对象的指针,而 fd 是这个数组的下标。
我们对文件进行操作时,系统调用,将fd传入内核,内核通过fd找到文件,对文件进行操作。
既然是数组下标,fd的类型为int, < 0 为非法值, >=0 为合法值。在linux中,一个进程默认可以打开的文件数为1024个,fd的范围为0~1023。可以通过设置,改变最大值。
在linux中,值为0、1、2的fd,分别代表标准输入、标准输出、标准错误输出。因为 0 1 2已经被linux使用了,通常在程序中打开的fd,是从3开始的。但我们在判断一个fd是否合法时,依然要使用>=0的判断标准。fd的分配原则,是从小到大,找到第一个不用的进行分配。
句柄泄露可能原因:
- socket socket()/accept();
- 数据库sqlite游标操作
- 文件流操作,检查后都有close;
- 硬件通讯,如串口,usb这些,是否正常关闭
通过排查,发现串口通讯没有正常关闭,创建太多fd,导致fd耗尽,最终影响数据库sqlite游标操作,出现了上面的异常CursorWindowAllocationException,数据Cursor创建也需要fd。
但是线上监控只是上报异常,没有指出哪里泄露,这时候需要通过物联网(IoT) MQTT(构建于Tcp/Ip协议的一种应用层协议,它的全称是Message Queuing Telemetry Transport)下发指令(adb)拉取fd信息,然后从终端设备上传到本地,再分析fd信息进行排查是哪里泄露。Java发送adb指令可以通过Runtime.getRuntime().exec()发送,再读取,需要注意发送的指令是否需要Root权限和结束符
Fd查询
1)Linux系统下,所有进程允许打开的最大fd数量。查询语句:
/proc/sys/fs/file-max
2)Linux系统下,所有进程已经打开的fd数量及允许的最大数量。查询语句:
/proc/sys/fs/file-nr
3)单个进程允许打开的最大fd数量.查询语句:
ulimit -n
4)单个进程(例如进程id为5454)已经打开的fd.查询语句:
ls -l /proc/5454/fd
操作步骤:
adb shell
su查看app包名的pid
ps | grep com.sjl.testu0_a280 11218 243 2997124 275288 SyS_epoll_ 7795a0eb80 S com.sjl.test注意:11218就是pid查看pid为3617的当前句柄数
ls -l /proc/3617/fd | wc -l
列出句柄被占用详细情况
ls -l /proc/3617/fd
or 进入目录查看
切换到15429进程的FD目录下
cd /proc/15429/fd然后在查看当前FD目录下文件描述符数量:
ls -l | wc -l94
通过观察查询文件描述符,如果文件描述符并没有被释放,说明这时候出现了FD泄露
小结
Fd泄露并不容易发现,但是规范编码,能减少大部分异常的发生,这是非常重要的。
下面是列出一些常见的优化和性能检测手段
- 内存泄露检测工具: LeakCanary 、Android Studio 的 Android Profiler 、eclispe的MAT
注意:memory leak会最终会导致out of memory。 - 检测在主线程运行的操作时长(卡顿检测):BlockCanary
- 检测方法耗时:Hugo(基于AOP实现)
- 检测工具,APP研发助手:dokit
- Native层崩溃的监听工具:BreakPad(不支持线上)
- Native Crash + Java Exception检测工具:Bugly(线上检测工具)、xCrash(不支持线上)
- 线上内存监控方案:KOOM(快手)
- 性能分析工具:TraceView、SysTrace(SysTrace用于收集可帮助您检查原生系统进程的详细系统级数据,例如CPU调度、磁盘活动、应用线程等,并解决掉帧引起的界面卡顿)
TraceView:
- 通过Android studio自带的traceview查看(Android profiler)。
- 通过Android SDK自带的Debug。(导出 trace 文件,通过Android Studio的profile打开该文件,可以知道有多少线程、具体方法的耗时)
- 通过DDMS中的traceview查看。
SysTrace:
在代码的开始位置加上tag
TraceCompat.endSection();
然后指定位置结束,即可以抓取到整个应用在此过程的相关信息
TraceCompat.beginSection("onCreate");
- 布局层级和性能分析工具:Hierarchy Viewer(已废弃)、Android Studio 的Layout Inspector(不支持查看布局性能)
Android数据库Cursor异常原因和Fd泄露分析相关推荐
- Android 数据库之Cursor
2019独角兽企业重金招聘Python工程师标准>>> 使用过 SQLite 数据库的童鞋对 Cursor 应该不陌生,如果你是搞.net 开发你大可以把Cursor理解成 Ado. ...
- Android异常总结---1.异常原因: java.lang.IllegalArgumentException: URI: content://com.android.contacts/con
1.异常原因: java.lang.IllegalArgumentException: URI: content://com.android.contacts/contacts1/data, call ...
- android异常:dialog窗口句柄泄露
抛出异常:Activity ... has leaked window com.android.internal.policy.impl.PhoneWindow$... that was orifin ...
- android数据库isnull,Android中SQLite数据库知识点总结
SQLite 数据库简介 SQLite 是一个轻量级数据库,它是D. Richard Hipp建立的公有领域项目,在2000年发布了第一个版本.它的设计目标是嵌入式的,而且占用资源非常低,在内存中只需 ...
- Android OOM出现常见原因及解决办法
手机在运行应用的时候,一直在崩溃,而这个异常就是OutOfMemory的错误,简称为OOM, 搞得我们也是极其的崩溃,最后 ,我们是通过网上搜集资料和代码走查的方式来优化解决的,这里,我就把我们收集到 ...
- android数据库三个方法有哪些,一个三年Android开发的总结 - 常见数据库问题总结...
Android应用开发离不开对SQLite数据库的操作,一般简单的增删改查,可以直接通过SQLiteDatabase进行,但是只要操作变得频繁,并且随着需求不断增加,你应该考虑使用ORM框架,至少使用 ...
- JVM内存管理概述与android内存泄露分析
一.内存划分 将内存划分为六大部分,分别是PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量池以及本地方法栈. 1.PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的 ...
- 应用与系统稳定性第三篇---FD泄露问题漫谈
cat /proc/pid/limits 查看最大打开文件Max open files cat /proc/pid/fd 查看打开文件 cat /proc/sys/kernel/threads-max ...
- android出现adpt2的问题,android - 尝试在空对象引用上调用接口方法'int android.database.Cursor.getCount()'[重复] - 堆栈内存溢出...
我确实有一个让我头疼的问题. 我通过自定义内容提供程序将我的城市的一些图像存储在sqlite数据库中. 但是,当我运行我的应用程序时,我得到一个空光标. Caused by: java.lang.Nu ...
最新文章
- node基础学习——操作文件系统fs
- 汇编语言--CPU对存储器的读写
- OpenJ_Bailian 4148 生理周期
- ML之HierarchicalClustering:自定义HierarchicalClustering层次聚类算法
- Android 应用框架
- 27.用zxing生成二维码
- 【华为云技术分享】华为云文档数据库服务DDS监控告警全新优化
- 微软推出新编程语言 Bosque,超越结构化程序设计
- Doule类型转成十六进制查看
- AMD授权GPU给Intel?苏姿丰:没有的事
- .net 统计图表和报表资源总结
- nyist 488 素数环
- maya golaem crowd群集测试 带自己录制的简易教程
- 深度学习需要计算机配置,深度学习工作站配置推荐
- 定义一个类描述分数,并提供加减乘除运算 以及分数化简
- sql查询每科成绩最高的人和分数
- 在线头像制作网站FaceYourManga
- apicloud 开发app 引用vue+element_ui的方法
- 网易互娱2017实习生招聘游戏研发工程师在线笔试第二场(图像处理)
- 《海洋测绘》:从地方坐标系到2000国家大地坐标系的转换方法