当前位置: 首页 > news >正文

oom和anr简单理解

目录

ANR(Application Not Responding)定义

ANR一般有三种类型:

处理办法

防患于未然

Out of Mana,法力耗尽。

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

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

ANR掌握这三个值:

如何调节阈值

什么情况会出现OOM呢?

防止OOM出现的一些注意点

总结


ANR(Application Not Responding)定义



在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。

OOM - Out of Memory,内存溢出

 

ANR一般有三种类型:

  1. KeyDispatchTimeout(5 seconds) — 按键或触摸事件在5秒内无响应

  2. BroadcastTimeout(10 seconds) — BroadcastReceiver在10秒内无法处理完成

  3. ServiceTimeout(20 seconds) — Service在20秒内无法处理完成

第一种类型很常见,就是你在UI线程里做了很多耗时的工作,导致UI阻塞住超过5秒,这个时候用户一定很气恼准备砸手机卖肾了,Android系统也会认为你的app做得实在是太烂了,赶紧提示用户把app结束掉,别让用户以为是安卓太烂了的缘故。

第二种也是一样,你刚刚接收到一个低电量的广播,要么赶紧弹一个框出来让用户注意电量(前台),要么赶紧开一个Service默默地杀掉耗电的app(后台),但是你若是在BroadcastReceiver里磨磨蹭蹭犹豫不决,Android系统也会认为你这个app简直是天秤座附体,赶紧回家纠结吧,别占着广播台了。

第三种比较少见,但也会出现,比如处理一些数据的时候算法不够优化,写大量文件啊,等等。

处理办法

  • 1和3的处理办法就是:开线程、开线程、开线程,然后用上一课我们学到的Handler去异步处理UI。
  • 2的话和上文提到的一样,要么转换成1的问题或者3的问题,不要停留在Broadcast里。

防患于未然

耗时的操作主要是I/O操作:

  • 网络请求

    当你准备进行网络操作的时候,想都不要想,一定要开线程。在Android2.x的时候还允许你在主线程里进行网络请求(只要不超过ANR的5秒限制),现在的Android4.x就不要想了,连ANR的机会都不会给你,直接报异常退出。

  • 文件读写

    当你用java的File操作的时候当然会警觉,会想到要开线程。但是有些坑你未必就能注意到:

  1. 数据库操作,尤其是可能会有大量批量的数据库操作的时候。

    (我表示曾经被坑过,即便你是开了线程,在大量数据库操作时的超级慢也会让人受不了,你需要搜索一下“SQLite的事务处理”,症结在这儿。

    ps:不只是SQLite,数据库在批量操作的时候都需要“事务处理” 。)

  2. 注意SharedPreferences,由于SharedPreferences封装的很好,所以很多时候往往会忘记这东西本质上是在读写xml文件啊!

    虽然一般用这种方式存储是很少的数据量,但依然不可小觑。还有个小细节也要改变一下以往的习惯:

    SharedPreferences sp=context.getSharedPreferences("xxx",context.MODE_PRIVATE);
    SharedPreferences.Editor editor=sp.edit();
    editor.putInt("num",1);
    // editor.commit(); 没错,旧方法是使用commit(),它是直接操作文件的
    editor.apply();    //推荐使用新方法apply(),它是异步的
  • 计算密集型操作

    没啥说的,异步,然后优化算法吧。如果计算结果不是急需要的话可以托管给服务端来处理嘛!

总结

总而言之,ANR的存在就是在不断提醒你,优化,优化,再优化。优化效率,优化视觉,优化体验。

 

 

Out of Mana,法力耗尽。

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

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

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

当然了,设置恰到好处的限制值也是很不容易的。拿我身边的手机做了个调查:我的烂酷派,给每个进程的内存限制是48M,而RAM同样是1G的旧华为分配了128M,魅族MX4是256M。

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,这里就不展示了,直接展示运行结果。

ANR掌握这三个值:

  • 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类联合使用实现。

 

防止OOM出现的一些注意点

 

  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的存在就是在不断提醒你,节约、节约、再节约。节约开销,随手关门,循环利用,养成良好的习惯,让资源去做更美好的事。

相关文章:

  • 分母有理化
  • run gradle task 进行多应用同时打包 debug和release,以及签名配置。
  • SDK,NDK,AVD,AMD简单理解
  • build.gradle文件介绍,gradle版本对应
  • Android 开发 读取excel文件 jxl.jar包
  • RecyclerView的简单使用在activity和fragment中
  • 好用的dialog工具类,单例实现,普通的Dialog和Textview设置Int型
  • tabLayout实现viewPager+Fragment
  • Android控件绑定代码快速生成工具 ,Android 开发Invalid byte 2 of 2-byte UTF-8 sequence. Textview设置空格
  • Android 开发execel文件在内存和外存传递
  • Android 开发 sqllite文件查看,SqlLite文件查看步骤
  • GreenDao查询,Querying
  • 新版gradle 找不到 greendao 报错 android studio 怎样查看导报文件 怎样打开db文件 android greendao 工具类使用以及数据更新
  • android.content.res.Resources$NotFoundException: String resource ID #0x1
  • 通过Interface:fragment向activity传值
  • JavaScript 如何正确处理 Unicode 编码问题!
  • @angular/forms 源码解析之双向绑定
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • JavaScript类型识别
  • Java多态
  • Next.js之基础概念(二)
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • supervisor 永不挂掉的进程 安装以及使用
  • Travix是如何部署应用程序到Kubernetes上的
  • Vultr 教程目录
  • webpack4 一点通
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 机器学习中为什么要做归一化normalization
  • 使用agvtool更改app version/build
  • 数据可视化之 Sankey 桑基图的实现
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 终端用户监控:真实用户监控还是模拟监控?
  • 第二十章:异步和文件I/O.(二十三)
  • #define 用法
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • $.each()与$(selector).each()
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (NSDate) 时间 (time )比较
  • (二)windows配置JDK环境
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (原)本想说脏话,奈何已放下
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .mysql secret在哪_MYSQL基本操作(上)
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .NET Core Web APi类库如何内嵌运行?
  • .NET学习教程二——.net基础定义+VS常用设置
  • @ModelAttribute 注解
  • @RequestBody详解:用于获取请求体中的Json格式参数
  • @staticmethod和@classmethod的作用与区别
  • [④ADRV902x]: Digital Filter Configuration(发射端)
  • [BZOJ2850]巧克力王国