许久没水文章了,今日忽然想水水。给我们分享一下一年多曾经遇到的一个无限缓存的 OOM 现象

首要

想必我们都知道OOM是啥吧,我就不扯花里胡哨的了,直接进入正题。先说一个背景故事,我司app扫码框架用的zxing,在很长一段时间曾经,做过一系列的扫码优化,稍微列一下跟今日主题相关的改动:

  1. 串行处理改成并发处理,zxing的原生处理流程是通过CameraManager获取到一帧的数据之后,通过DecodeHandler去处理,处理完结之后再去获取下一帧,咱们给改成了线程池去调度:
  • 单帧decode使命入行列之后当即获取下一帧数据
  • 二维码辨认成功则中止其他解析使命
  1. 为了有更大的辨认区域,挑选对整张拍摄图片进行解码,确保中心框框没对准二维码也能辨认到

现象

当时测试反馈,手上一个很古老的 Android 5.0 的机器,翻开扫一扫必崩,一看错误栈,是个OOM

机器找不到了,我就不贴现象的仓库了(埋在韶光里了,懒得挖了)。

排查OOM三板斧

板斧一、 通过一定手法,抓取崩溃时的或许崩溃前的内存快照

咦,一年前的hprof文件还在?的确被我找到了。。。

从图中咱们能取得哪些信息?

  1. 用户OOM时,byte数组的 java 堆占用是爆炸的

  2. 用户OOM时,byte数组里,有大量的 3Mbyte数组

  3. 3Mbyte 数组是被 zxingDecodeHandler$2 引用的

板斧二、从内存对照动身,斗胆猜想找到坏死本源

咱们已然知道了 大目标 是被 DecodeHandler$2 引用的,那么 DecodeHandler$2 是个啥呀?

mDecodeExecutor.execute(new Runnable() {
            @Override
            public void run() {
                for (Reader reader : mReaders) {
                    decodeInternal(data, width, height, reader, fullScreenFrame);
                }
            }
        });

所以稍微滚动一下脑瓜子就能知道,必定是堆积了太多的 Runnable,每个Runnable 持有了一个 data 大目标才导致了这个OOM问题。

可是为啥会堆积太多 Runnable 呢?结合一下只有 Android 5.0 机器会OOM,咱们斗胆猜想一下,便是因为这个机器消费(或许说解码)单张 Bitmap 太慢,同时像上面所说的,咱们单帧decode使命入行列之后当即获取下一帧数据并入队下一帧decode 使命,这就导致大目标堆积在了LinkedBlockingDeque中。

OK,到这里原因也清楚了,改掉就完事了。

板斧三、 吃个口香糖舒缓一下心情

呵呵…

处理方案

处理方案其实很简单,从问题动身即可,问题是啥?我出产面包速度是一天10个,一个一斤,可是一天只能吃三斤,那岂不就一天就会多7斤囤货,假如囤货到了100斤地球会毁灭,怎样处理呢?

  1. 吃快点,一天吃10斤
  2. 少出产点,要么出产个数削减,要么出产单个分量削减,要么二者一起
  3. 出产前检查一下吃完没,吃完再出产都来得及,真实不行定个阈值觉得不行吃了再出产嘛。

那么自然而然的就大概知道有哪几种处理办法了:

  1. 出产的小点 – 隔几帧插一张全屏帧即可(假如要保存不在框框内也能解码的特性的话)
  2. 出产前检查一下吃完没 – 线程池的线程闲暇时,才去 enqueue decode 使命
  3. 出产单个分量削减 – 约束行列大小
  4. blalala

总结

装腔作势的总结一下。

这个比如是一年前遇到的,今日想水篇文章又忽然想到了这个事就拿来写写,我总结为:线程池调度 + 进堵塞行列单使命数据过大 + 处理使命过慢

线程池调度使命是啥场景?

  • 有个 Queue,来了使命,先入队
  • 有个 ThreadPool ,闲暇了,从 Queue 取使命。

那么,当入队的数据结构占内存太大,且 ThreadPool 处理速度小于 入队速度呢?就会形成 Queue 中数据越来越多,直到 OOM

扫一扫完美的满意了上面条件

  • 入队频率足够高

  • 入队目标足够大

  • 处理速度足够慢。

在这个比如中,做的不足的地方:

  1. 追求并发未考虑机器功能

  2. 大目标处理不行谨慎

当然,总结是为了避免未来相同的惨案发生,我们可以想想还会有什么相似的场景吧,滚动一下聪明的小脑袋瓜~

未来展望

装腔作势展望一下,未来展望便是,今后有空多水水贴子吧(不是多水水贴吧)。