• OOM 类型
  • OOM 错误原因
  • OOM 定位问题

0x0001. OOM 类型

  1. Java 堆栈溢出,多数原因是内存泄露或者图片过大

    java.lang.OutOfMemoryError
    Failed to allocate a 3225612 byte allocation with 1716008 free bytes and 1675KB until OOM

  2. Java 堆栈溢出,此类 bug 最好让 Glide 之类的图片框架进行统一加载图片,同上

    java.lang.OutOfMemoryError
    OOM allocating scaled Bitmap with dimensions 1056 x 330

  3. 虚拟内存不足或者单个进程中的线程数到达上限(堆中内存充足,却 OOM,前者可能性极小忽略)

    java.lang.OutOfMemoryError
    pthread_create (1040KB stack) failed: Try again

  4. JNI 报错,进程打开文件数量达上限或虚拟内存不足(堆中内存充足,却 OOM,后者可能性极小忽略)

    java.lang.OutOfMemoryError
    Could not allocate JNI Env

  5. 因为 OOM 引起的 OOM,解决上述三种问题,基本就不会出现此类 bug

java.lang.OutOfMemoryError
OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available

0x0002. OOM 错误原因

  1. 情况一,多数情况是解决内存泄露,可以参考内存泄露分析定位,其次是大图处理

    常见图片处理小技巧

    1. Recyclerview 快速滑动时,针对9宫格类 App,可以暂停图片加载 Glide.with(this).pauseRequests()
    2. 各个 ViewBackground 如果是图片,最好也用 Glide 统一载入
    3. 图片尽量用 webp
  2. 情况二,只在 Android 9.0 以上报出,核心是图片统一用 glide 类似的框架处理

    上述两种情况,终极原因都是申请的内存大于 Runtime.getRuntime().maxMemory()

  3. 情况三,源于 linux 的系统对单个进程开启线程数量的限制

    可以监控 /proc/[pid]/task,定位具体线程来源,具体分析参考此文章

    /proc/sys/kernel/threads-max

    规定了每个进程创建线程数目的上限

    /**
    * 获取 task 数量
    * 用于解决线程数量暴增导致的 OOM
    */
    public static int getTaskCount(){
      File taskFile = new File("/proc/" + Process.myPid() + "/task");
      File[] files = taskFile.listFiles();
      int length = files.length; 
    }
    
  4. 情况四,源于 linux 的系统对单个进程能打开文件数量的限制

    可以监控 /proc/[pid]/fd ,定位具体原因,具体分析参考此文章

    对比 /proc/pid/limits 内容,获取 Max open files

    Max open files表示每个进程最大打开文件的数目,进程每打开一个文件就会产生一个文件描述符 fd(监控 /proc/pid/fd 内的文件数即可)

    ...
    /**
    * 获取 PD 数量
    * 用于解决文件打开数量暴增导致的 OOM
    */
    public static int getPDCount(){
      File fdFile = new File("/proc/" + Process.myPid() + "/fd");
      File[] files = fdFile.listFiles();
      int length = files.length; 
    }
    
    public static final String prefix = "Max open files";
    public static String PID_LIMITS =  "cat /proc/" + Process.myPid() + "/limits";
    /**
    * 获取进程文件限制数量
    * 用于解决文件打开数量暴增导致的 OOM
    */
    public static String getLimitsInfo() throws IOException {
      Runtime runtime = Runtime.getRuntime();
      java.lang.Process proc = runtime.exec(PID_LIMITS);
      BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
      StringBuffer stringBuffer = new StringBuffer();
      String line = null;
      while ((line = in.readLine()) != null) {
           stringBuffer.append(line);
           if (line.startsWith(prefix)) return line;
      }
      return "";
    }
    ...
    
    
  5. 情况五,虚拟内存分配失败(mapp 失败),参考此文

0x0003. OOM 定位问题

  1. 线下定位,直接进行分析即可

  2. 线上采集定位,较为麻烦,具体方案可以参考 美团的 Probe 设计方案,考虑到 LeakCanary 用了新的shark解析hprof 文件,可以在美团的方案上进行升级

文章参考

不可思议的OOM

Probe:Android线上OOM问题定位组件

Android 创建线程源码与OOM分析