今天很恼火, 大致状况是这样的, 程序直接进入数据库写操作InsertDBActivity中,异步完成80W条数据的插入, 不会OOM; 当我在进入InsertDBActivity之前先来了个用户介绍页(滑动预览6张图片), 然后换了个高清的背景图,够炫,但是悲剧的一幕发生了 在log中可以明显的看到 数据插入到40W多的时候 OOM了! 什么原因呢? 我debug追踪了好久,代码没怎么改动呀,,任何地方都断点看了, 就是老是出现内存溢出, 我就纳闷了······· 只有一种情况, 我加的这些代码中有问题! 可是我也没加什么东西呀? 不对·· 有6张介绍页!
不会吧? 6张介绍页加起来内存才占了不到1M,, 这个引起的oom? 与测试机MI2的内存比起来显然微不足道, 但是没有其他地方动过啊! 哦 还有一张高清的背景图·· 但是我看了下工程背景文件大小, 也不到1M,, 怒了··········
遂········
1.将介绍页屏蔽掉,
2.将高清背景图去掉,
3.将数据量保持在80W .
再走一遍 果然, 没事嘞~~ 但是······
还原1,2, 将数据量再提升至80W, 内存溢出;
还原1,2, 将数据量降至20W, 不溢出;
还原1,2, 将数据量提升至40W, 内存溢出······ 我去 有完没完啊
然后就乖乖的检查代码执行流程, 查找一切可以导致内存溢出的可能···············
跟踪到activity的跳转一行, 我怎么看都像少了点什么, 但又说不上来, activity不是压栈管理嘛, 也就没网这上面想, 大爷的, 问题就出在这里············
当activity入栈处于非栈顶,activity中的资源未消失,等待出山,只有等到内存资源不够用的时候系统才会自动清理; 然后就联想到Java不是有垃圾回收机制来着, 然后就理所当然的认为没问题, 再说, MI2的内存那么大, 这点资源占的内存微乎其微, 然后就理所当然的在排查着错········
int dataIsLoaded = sp.getIntValue(ConstancessUtil.DATA_LOADED); if(username.equals("1")) {//跳转至中层领导界面 // new LoginAsyncTask(KPILoginActivity.this).execute(username,password,String.valueOf(dataIsLoaded)); if(dataIsLoaded == 1) { startActivity(new Intent(this,MainLSPKPIActivity.class)); finish(); } else if(dataIsLoaded == 0) { startActivity(new Intent(this,TestDBActivity.class)); finish(); } } else if(username.equals("2")) {//跳转至高层领导界面 // new LoginAsyncTask(KPILoginActivity.this).execute(username,SeniorLeaderMainActivity.class)); finish(); } else if(dataIsLoaded == 0) { startActivity(new Intent(this,TestDBActivity.class)); finish(); } }
当我看见跳转之后没有 finish();的时候我就想了, 是不是就这点资源泄漏, 未及时清理的原因, 遂手动加上了销毁finish();; 经过测试, 可以了;
大哥不是吧, 这也行??
手机内存 & 运行时内存 什么关系? 有待深究··
我回头想了想, 导致自己思维混乱的几点:
1. 书写太随意, 很多问题当想到点上的时候硬是想当然的认为不可能, 没有经过实践的检验;
2.太教条, 将Java许下的美好诺言信以为真-- 垃圾自动回收--
3.要大胆尝试, 大胆质疑, 哪怕是自己写的自认为很得意的代码, 也要有推翻重来的勇气!
其实回头看, 就是用finish()方法将前一个activity销毁, 其资源也被释放, 不占据手机的运行内存; 如果没有finish()方法, activity中的所有资源在栈中,资源未释放,依然占据着内存, 可能一般情况下不会出现内存泄露,毕竟现在的手持设备的硬件配置都挺高, 但是当数据量刚刚好的时候··········它就偏偏发生了, 而且除了这几张图片, 别无他因; 所以不要凭借着现在硬件的优势而丧失了我们编码的优秀原则, 移动端不是PC端,本身就是一种限制!
另···附sqlite亲测参数以供参考(不同设备可能有所差异):
测试机器: MI2
数据表字段:18个全满
插值--
-------记录数------------插值耗时-------------生成db文件大小 --------普通查询耗时 -------优化查询耗时-----
535680条 (193-238)s 38.51M 8S扫描一次 2S内
803520条 443s左右 57.54M 10s+ ANR 3S内
807557条 临界溢出值 ******** ** **
100W outofmemory ******** ** **
其实数据量小的时候, 怎么查询都可以; 但是数据量一旦突破几十万, 甚至过百万, 就需要优化了;
如果不优化查询, 时间很长, 不太好, 但是如果优化的话, 查询逻辑都要重新推翻重新设计; 有时候真的很无奈 , 真的。
这个项目的设计,本身有存在些不妥, 但是没有办法, 客户需求; 但是手机毕竟是个手机, 内存有限, 处理能力也相当**, 用手机来存储百万级的数据是不是有点****
我到现在查询优化的还不够快, 还能更快! 这是要将手机的cpu烧了的节奏吗? 还有, 目标数据源是100W-300W之间, 我的个天呐, 现在80多w的时候就OOM了, 怎么整!!!!!!
本身表设计的时候存在着父子节点查询, 即树的概念;
优化逻辑过程:
原逻辑: 用 select * from region where uid like ‘parentID%’ 即可以通过当前的ReginID查出其所有子节点;然后经过id位数来进行筛选操作;
数据量小的时候这种查询不要太方便, 不要太爽; 但是数据量大的时候, 要加索引进行优化查询, 很不幸, like函数与索引不兼容---即like()无效······
怎么办!!!!!!!!!!!!!!!!!!!!!! 换查询逻辑!!!!!!!!
改进逻辑: 用in代替like! 这是我最不愿意用的函数!select * from region where uid in('child1',child2'','child3' ···); 但事实就是这样, 你越是不愿搭理的事物,有时候它反而还跟你叫上真儿了, 非它不方便! 关键是人家与索引不冲突哇!!!
结论············ 用index+in() 代替原来的万能like(%)、查询速度成倍提升! 但是随之而来的是插入数据的时间变长; 看你的侧重了、
我们经理常说, 做一个项目就像扒一层皮, 我此时想说, 做一个项目就像杀我一次, wqnmlgbd,, 呵呵 但是学到的东西远不是书本中能提供的了的、
人说痛并快乐着, 那就是傻子, 痛的时候停下来休息下, 养养伤, 伤好了才有心情去寻找快乐嘛 对不对?
我自己很喜欢初涉IT泥潭的一句话, 也是我自己凝练的······················
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~唯有压力才能突破, 才能距破茧成蝶之日更近一步~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~