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://cyhhao.zhusun.in

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

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

    恼人的ANR 早先年用Android的时候,就连很多知名的app也总是莫名其妙崩溃,好像手机快的时候会崩溃,手机卡的时候app会卡死.卡死的时候会弹出来一个框,询问是要结束app还是继续等待.这就是A ...

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

    BBB板第三课:Debian7.5系统安装和远程控制BBB板 由于BBB板系统是Debian 7.4.据说使用Debian系统能够实现非常多BBB板的无缝连接.能够更好的学习和控制BBB板,所以就决定 ...

  3. Git速成学习第三课:创建与合并分支

    本来第三课想记录一下远程仓库的创建与克隆0.0但是想了想还是不写了. 这里写一下分支管理中的创建与合并. Git速成学习笔记整理于廖雪峰老师的官网网站:https://www.liaoxuefeng. ...

  4. CodeIgniter框架入门教程——第三课 URL及ajax

    本文转载自:http://www.softeng.cn/?p=74 这节课讲一下CI框架的路由规则,以及如何在CI框架下实现ajax功能. 首先,先介绍CI框架的路由规则,因为CI框架是在PHP的基础 ...

  5. SQL初级第三课(下)

    我们续用第三课(上)的表 辅助表 Student                   Course               Score                    Teacher Sno ...

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

    shellKali Linux Web 渗透测试— 初级教程(第三课) 文/玄魂 目录 shellKali Linux Web 渗透测试—初级教程(第三课) 课程目录 通过google hack寻找测 ...

  7. NeHe OpenGL教程 第三课:颜色渲染

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. 【Linux探索之旅】第二部分第三课:文件和目录,组织不会亏待你

    内容简介 1.第二部分第三课:文件和目录,组织不会亏待你 2.第二部分第四课预告:文件操纵,鼓掌之中 文件和目录,组织不会亏待你 上一次课我们讲了命令行,这将成为伴随我们接下来整个Linux课程的一个 ...

  9. 【Web探索之旅】第三部分第三课:协议

    内容简介 1.第三部分第三课:协议 2.第四部分预告:Web程序员 第三部分第三课:协议 之前的课,我们学习了Client-Server模型的客户端语言和服务器语言. 客户端语言有HTML,CSS和J ...

随机推荐

  1. ORA-00257: archiver error. Connect internal only, until freed——解决

    参考http://www.2cto.com/database/201109/104615.html, 开启归档后,操作一个大表迁移表空间,执行了1个多小时没完成就手动给中断了,但是再次用plsql登陆 ...

  2. asp 时间倒数后按钮可用

    <asp:Button runat="server" ID="btn" Text="免费获取验证码" onclick="bt ...

  3. define 与 inline

    define 就是代码替换,在编译阶段进行简单的代码替换,大量用于宏定义开关,以及定义表达式和常量,如: 1.开关定义 #define CONFIG_OPENED 使用: #ifdef CONGFIG ...

  4. Three levels at which any machine carrying out an Information-Processing task must be understood

    1. Computational theory What is the goal of computation, why is it appropriate, and what is the logi ...

  5. Android xml 格式 随笔

    打包的时候Android xml文件会由字符格式(utf-8编码)转换为二进制格式.具体如:http://blog.csdn.net/jiangwei0910410003/article/detail ...

  6. 移动应用安全开发指南(Android)--完结篇(http://www.bubuko.com/infodetail-577312.html)

    1.认证和授权 概述 认证是用来证明用户身份合法性的过程,授权是用来证明用户可以合法地做哪些事的过程,这两个过程一般是在服务器端执行的,但也有的APP出于性能提升或用户体验等原因,将其做在客户端完成, ...

  7. C#标准响应数据

    public HttpResponseMessage UpdateModule(Mode mode) { var response = Process.Instance.ExecuteString(( ...

  8. Windows Tomcat7.0 安装 Solr

    准备工作 1.下载Tomcat7.0 ,apache-tomcat-7.0.67.exe,安装目录如下:C:\workspace\Tomcat7.0\ 2.下载Solr 5.2,solr-5.2.0. ...

  9. typeof instanceof 之间的区别总结

        typeof   它返回值是一个字符串,该字符串说明运算数的类型. a=1; b=true; c="c"; d=function(){ console.log(" ...

  10. SAP ECC FI配置文档

    SAP ECC 6.0 Configuration Document Financial Accounting (FI) Table of Content TOC \O "1-2" ...