• 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. 各个 View 的 Background 如果是图片,最好也用 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分析