【第三课】ANR和OOM——贪快和贪多的后果(下)

Out of Mana,法力耗尽。

内存就像法力,耗尽了就什么都不能做了。有时候一个应用程序占用了太大的内存,超过了Android系统为你规定的限制,那么系统就会干掉你,以保证其他app有足够的内存。俗称内存溢出(Out Of Memory)。(其实不止Android系统,内存溢出本身说的就是java虚拟机的事。)

这个内存的限度究竟是多少呢?

有人说是16M,有人说是32M。事实上,这个是因系统而异的,系统又因硬件设备而异。通常来说物理RAM越大的手机,系统制作者会设置宽松一点的内存限制。

当然了,设置恰到好处的限制值也是很不容易的。拿我身边的手机做了个调查:

  • 我的烂酷派 5891 2013年 4核 1G的RAM
  • 我的旧华为 c8825d 2012年 2核 1G的RAM
  • 别人帅呆了的魅族MX4 2014年 8核 2G的RAM

我的烂酷派,给每个进程的内存限制是48M,而RAM同样是1G的旧华为分配了128M,魅族MX4是256M。

为啥我的华为和酷派都是1G的RAM,酷派的cpu更好一些,我要说酷派“烂”呢?因为我平时用的时候,大一点的游戏、APP点开就会闪退,而配置差一些的华为却可以运行占用内存在128M以内的游戏。(ps:其实不是因为这一点讨厌酷派,是因为酷派有后门啊!大家不要买酷派!电信白送的建议刷机!)

ps:但是呢从另一个方面讲,假设每个进程都恰好占用了满限制的内存空间,那么酷派的手机最少可以开21个进程,而华为和魅族最少只能有8个进程。虽然这样看起来21进程和8进程都挺多,但是除去后台的杀不掉的进程,从体验上讲,保证小内存的app能够运行起来,可以让多任务的体验更好一些……

这个限制值是怎么测出来的呢?

直接贴代码(Activity内的代码):

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Button button= (Button) findViewById(R.id.ok);
    final TextView text=(TextView)findViewById(R.id.text);
    button.setText("查看内存");
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            String str="";
            str="最大可得到内存:"+Runtime.getRuntime().maxMemory()/1024/1024+"M"+"\n";
            str+="目前占用内存:"+Runtime.getRuntime().totalMemory()/1024/1024+"M"+"\n";
            str+="目前占用内存中空闲部分:"+Runtime.getRuntime().freeMemory()/1024/1024+"M"+"\n";
            text.setText(str);
        }
    });
}

布局文件为一个TextView和一个Button,这里就不展示了,直接展示运行结果。

掌握这三个值:

  • maxMemory

    最大可得到内存,指的是这个进程能从系统得到的最多的内存空间,超过这个值就会OOM。

  • totalMemory

    目前该进程所占用的内存有多大,很好理解。

  • freeMemory

    从字面理解很容易误解,让人以为是进程还能使用的剩余空间。其实不是这样,进程从系统挖来了totalMemoty这么大的空间,但是并没有完全使用,其中还有freeMemory这么大的空间只是挖来了,并没有实际的使用它们。等到程序真正开始使用这些空间的时候,freeMemory就开始减少,当减到0的时候就会去挖更多的空间来,依然会多挖一些来备用。以此类推,直到达到maxMemory,然后OOM,Duang~

    有一个很简单有效的方法来帮助你理解这一过程,你在onClick()里除了显示之外,加上一个循环:

    for(int i=1;i<10000;i++){
        list.add(""+i);//像一个全局List里每次加10000条
    }

    很快你就能看的这三个值是怎么变化的了,到最后也能看到OOM长什么样了……如果你把10000稍微增大一点,观察logcat或许能看到ANR的警告。

那么掌握这三个数值有什么用呢?我们是不是在开发app的时候讲不同的内存管理策略写到程序里,智能地根据实际阈值来防止OOM,提升体验。

如何调节阈值?

在/system/build.prop文件中,文件很长仔细找:

  • dalvik.vm.heapstartsize——app启动起始获得的内存
  • dalvik.vm.heapgrowthlimit——OOM的阈值
  • dalvik.vm.heapsize——如果设置了heapgrowthlimit则无作用

什么情况会出现OOM呢?

情况很多种,一次性开了太大的内存空间、某些对象使用完成后没有及时断开引用让GC自动回收等等。这里涉及到java的GC垃圾回收机制,这里不多讲了,自己先去谷歌java的回收机制,强引用、软引用、弱引用、虚引用,在这里只做简单的科普吧:

强引用: 通常我们编写的代码都是Strong Ref,eg :Person person = new Person(“sunny”);不管系统资源有多紧张,强引用的对象都绝对不会被回收,即使他以后不再用到。

软引用:只要有足够的内存,就一直保持对象。一般可用来实现缓存,通过java.lang.r.efSoftReference类实现。内存非常紧张的时候会被回收,其他时候不会被回收,所以在使用之前需要判空,从而判断当前时候已经被回收了。

弱引用:通过WeakReference类实现,eg : WeakReference p = new WeakReference(new Person(“Rain”));不管内存是否足够,系统垃圾回收时必定会回收。

虚引用:不能单独使用,主要是用于追踪对象被垃圾回收的状态。通过PhantomReference类和引用队列ReferenceQueue类联合使用实现。

参考资料:http://blog.csdn.net/vshuang/article/details/39647167

防止OOM出现的一些注意点

本来想好好总结一番的,但是搜了一下发现前人总结的太全面了,所以厚着脸皮直接粘贴过来(感谢前辈):

出处:http://blog.csdn.net/vshuang/article/details/41381109

  1. 频繁的使用static关键字修饰

    很多初学者非常喜欢用static类static变量,声明赋值调用都简单方便。由于static声明变量的生命周期其实是和APP的生命周期一样的(进程级别)。大量的使用的话,就会占据内存空间不释放,积少成多也会造成内存的不断开销,直至挂掉。static的合理使用一般用来修饰基本数据类型或者轻量级对象,尽量避免修复集合或者大对象,常用作修饰全局配置项、工具类方法、内部类。

  2. BitMap隐患

    Bitmap的不当处理极可能造成OOM,绝大多数情况应用程序OOM都是因这个原因出现的。Bitamp位图是Android中当之无愧的胖子,所以在操作的时候必须小心。

    2.1. 及时释放recycle。由于Dalivk并不会主动的去回收,需要开发者在Bitmap不被使用的时候recycle掉。

    2.2. 设置一定的压缩率。需求允许的话,应该去对BItmap进行一定的缩放,通过BitmapFactory.Options的inSampleSize属性进行控制。如果仅仅只想获得Bitmap的属性,其实并不需要根据BItmap的像素去分配内存,只需在解析读取Bmp的时候使用BitmapFactory.Options的inJustDecodeBounds属性。

    2.3. 最后建议大家在加载网络图片的时候,使用软引用或者弱引用并进行本地缓存,推荐使用android-universal-imageloader或者xUtils。

  3. 页面背景图

    在布局和代码中设置背景和图片的时候,如果是纯色,尽量使用color;如果是规则图形,尽量使用shape画图;如果稍微复杂点,可以使用9patch图;如果不能使用9patch的情况下,针对几种主流分辨率的机型进行切图。

  4. View缓存

    在ListView和GridView中,列表中的很多项(convertView)是可以重用的,不需要每次getView就重新生成一项。另外,页面的绘制其实是很耗时的,findViewById也比较慢。所以不重用View,在有列表的时候就尤为显著了,经常会出现滑动很卡的现象。

  5. 引用地狱

    Activity中生成的对象原则上是应该在Activity生命周期结束之后就释放的。Activity对象本身也是,所以应该尽量避免有appliction进程级别的对象来引用Activity级别的对象,如果有的话也应该在Activity结束的时候解引用。如不应用applicationContext在Activity中获取资源。Service也一样。

  6. BroadCastReceiver、Service 解绑

    绑定广播和服务,一定要记得在不需要的时候给解绑。

  7. handler 清理

    在Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null);取消所有的消息的处理,包括待处理的消息;

  8. Cursor及时关闭

    在查询SQLite数据库时,会返回一个Cursor,当查询完毕后,及时关闭,这样就可以把查询的结果集及时给回收掉。

  9. I/O流

    I/O流操作完毕,读写结束,记得关闭。

  10. 线程

    线程不再需要继续执行的时候要记得及时关闭,开启线程数量不易过多,一般和自己机器内核数一样最好,推荐开启线程的时候,使用线程池。

  11. String/StringBuffer

    当有较多的字符创需要拼接的时候,推荐使用StringBuffer。

其中我认为BitMap的隐患、View缓存需要着重讲解一下,其他的平时写代码的时候要养成良好的习惯,可以避免不必要的麻烦。不过准备在以后几讲中再详细讲解,这课的篇幅有点过长了。

其实在实际应用当中,对于View的缓存,ListView、GridView每次在写Adapter的时候就已经养成了优化的习惯,并且固定下来作为基本的写法。

而BitMap图片的优化,在明白原理之前可以尝试使用一些开源的库,如文中所说的android-universal-imageloader,体会一下用和不用的区别,然后再深入源码,学习它是如何实现图片异步加载、压缩、缓存的。

总结

总而言之,OOM的存在就是在不断提醒你,节约、节约、再节约。节约开销,随手关门,循环利用,养成良好的习惯,让资源去做更美好的事。

原文来自个人博客:【第三课】anr和oom-贪快和贪多的后果(下)

by:cyhhao http://blog.zhusun.in/cyhhao/

时间: 2024-10-07 15:34:13

【第三课】ANR和OOM——贪快和贪多的后果(下)的相关文章

【第三课】ANR和OOM——贪快和贪多的后果(上)

恼人的ANR 早先年用Android的时候,就连很多知名的app也总是莫名其妙崩溃,好像手机快的时候会崩溃,手机卡的时候app会卡死.卡死的时候会弹出来一个框,询问是要结束app还是继续等待.这就是ANR(Application Not Responding)无疑了. ANR一般有三种类型: KeyDispatchTimeout(5 seconds) — 按键或触摸事件在5秒内无响应 BroadcastTimeout(10 seconds) — BroadcastReceiver在10秒内无法处

BeagleBone Black 板第三课:Debian7.5系统安装和远程控制BBB板

BBB板第三课:Debian7.5系统安装和远程控制BBB板 因为BBB板系统是Debian 7.4,据说使用Debian系统可以实现很多BBB板的无缝连接,可以更好的学习和控制BBB板,所以就决定下载Debian7.5系统安装,采用虚拟机的安装方式. 一.系统安装 1.我下载了Debian7.5 32位系统,有三张DVD盘,网上有不少安装资料了,我这里就不详细介绍安装过程了.不过有一点可能很多人都会遇到的问题,就是安装过程中提示插入光盘的问题.虚拟机是Vmware workstation 10

【C语言探索之旅】第三部分第三课:SDL开发游戏之显示图像

内容简介 1.第三部分第三课: SDL开发游戏之显示图像 2.第三部分第四课预告: SDL开发游戏之事件处理 第三部分第三课:SDL开发游戏之显示图像 上一课中,我们学习了如何加载SDL库(SDL_Init),释放SDL库(SDL_Quit),如何打开一个窗口(Window),如何使用表面(Surface). 这些都是SDL库最最基本的操作.暂时,我们只会给窗口自带的表面上点颜色,好像挺乏味的. 这一课我们来学习如何插入图片.上一课我们说过,SDL中绘制图样需要在Surface上进行.Surfa

【Linux探索之旅】第三部分第三课:监视系统活动,滴水不漏

内容简介 1.第三部分第三课:监视系统活动,滴水不漏 2.第三部分第四课预告:后台运行及合并多个终端 监视系统活动,滴水不漏 经过上一课(<[Linux探索之旅]第三部分第二课:流.管道.重定向,三管齐下>)的锤炼,现在大家对Linux的命令行应该有了新的认识,而且水准大概已经提高到了一个不错的档次了.如果你还没有,快,快去给我练习去~ 上一课算是比较难的,大家都辛苦了.所以这课给大家轻松一下,可以愉快地学完. 放眼现在的操作系统,基本都是多任务操作系统了,Linux当然也不例外.因此,Lin

第三课 文件系统(上)

unix_c_03.txt====================第三课 文件系统(上)====================一.系统调用------------应用程序 -----------+| |v |各种库 |(C/C++标准库.Shell命令和脚本. |X11图形程序及库) || |v |系统调用 <----------+(内核提供给外界访问的接口函数,调用这些函数将使进程进入内核态)|v内核(驱动程序.系统功能程序)1. Unix/Linux大部分系统功能是通过系统调用实现的.如o

java工程开发之图形化界面之(第三课)

上面我们讲述了通过JOptionPane进行文本I/O操作,一个是通过JOptionPane来获取的 参考链接:http://blog.sina.com.cn/s/blog_993d254201013pgh.html#cmt_3339216 JOptionPane类 1.属于javax.swing 包. 2.功能:定制四种不同种类的标准对话框. ConfirmDialog 确认对话框.提出问题,然后由用户自己来确认(按"Yes"或"No"按钮) InputDialo

shellKali Linux Web 渗透测试— 初级教程(第三课)

shellKali Linux Web 渗透测试— 初级教程(第三课) 文/玄魂 目录 shellKali Linux Web 渗透测试—初级教程(第三课)... 1 课程目录... 1 通过google hack寻找测试目标... 2 一个asp站点的sql注入... 3 一个php站点的sql注入... 4  课程地址:点击 课程目录 两个基本案例,以sql注入入手,目标为熟悉基本的思路,关注细节信息. 关于google hack,web 扫描,sql注入更详细和复杂的内容后续教程会专门讲解

【C语言探索之旅】 第二部分第三课:数组

内容简介 1.课程大纲 2.第二部分第三课: 数组 3.第二部分第四课预告:字符串 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三个游戏. C语言编程基础知识 什么是编程? 工欲善其事,必先利其器 你的第一个程序 变量的世界 运算那点事 条件表达式 循环语句 实战:第一个C语言小游戏 函数 练习题 习作:完善第一个C语言小游戏 C语言高级技术 模块化编程 进击的指针,C语言王牌 数组 字符串 预处理 创建你自己的变量类型 文件读写 动态分配

【Linux探索之旅】第一部分第三课:測试并安装Ubuntu

内容简单介绍 1.第一部分第三课:測试并安装Ubuntu 2.第一部分第四课预告:磁盘分区 測试并安装Ubuntu 大家好,经过前两个比較偏理论(是否想起了带着瓜皮帽,手拿折扇的老学究,或者腐儒)的课程,这第三课我们就正式进入实战啦. 可能不少朋友没使用过Linux这个操作系统,那么这一课就是见识一下它的庐山真面目的时候了. 我们这个系列课程所使用的Linux发行版是Ubuntu,由于其使用广泛.技术支持全面.文档完整,另一个非常关键的原因就是Ubuntu的配色偏"土豪金"啊,有木有.