关于 OOM
OOM
类型OOM
错误原因OOM
定位问题
0x0001. OOM
类型
-
Java 堆栈溢出,多数原因是内存泄露或者图片过大
java.lang.OutOfMemoryError
Failed to allocate a 3225612 byte allocation with 1716008 free bytes and 1675KB until OOM -
Java 堆栈溢出,此类 bug 最好让 Glide 之类的图片框架进行统一加载图片,同上
java.lang.OutOfMemoryError
OOM allocating scaled Bitmap with dimensions 1056 x 330 -
虚拟内存不足或者单个进程中的线程数到达上限(堆中内存充足,却 OOM,前者可能性极小忽略)
java.lang.OutOfMemoryError
pthread_create (1040KB stack) failed: Try again -
JNI
报错,进程打开文件数量达上限或虚拟内存不足(堆中内存充足,却 OOM,后者可能性极小忽略)java.lang.OutOfMemoryError
Could not allocate JNI Env -
因为 OOM 引起的 OOM,解决上述三种问题,基本就不会出现此类 bug
java.lang.OutOfMemoryError
OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
0x0002. OOM
错误原因
-
情况一,多数情况是解决内存泄露,可以参考内存泄露分析定位,其次是大图处理
常见图片处理小技巧
- Recyclerview 快速滑动时,针对9宫格类 App,可以暂停图片加载
Glide.with(this).pauseRequests()
- 各个
View
的Background
如果是图片,最好也用Glide
统一载入 - 图片尽量用
webp
- Recyclerview 快速滑动时,针对9宫格类 App,可以暂停图片加载
-
情况二,只在
Android 9.0
以上报出,核心是图片统一用glide
类似的框架处理上述两种情况,终极原因都是申请的内存大于
Runtime.getRuntime().maxMemory()
-
情况三,源于
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; }
-
情况四,源于
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 ""; } ...
-
情况五,虚拟内存分配失败(
mapp
失败),参考此文
0x0003. OOM
定位问题
-
线下定位,直接进行分析即可
-
线上采集定位,较为麻烦,具体方案可以参考 美团的
Probe
设计方案,考虑到LeakCanary
用了新的shark
解析hprof
文件,可以在美团的方案上进行升级