【踩坑实践】RxJava使用不当引起的OOM

问题背景:

最近负责的网络库,在线上崩溃偶尔会报OOM,崩溃栈类似:

java.lang.OutOfMemoryError: Could not allocate JNI Env

这样就很奇怪,为啥OOM跟JNI Env有关呢?

一般认为的OOM,是虚拟机内存不足的时候发生,但正常来说,内存不足的OOM会类似:

java.lang.OutOfMemoryError: Failed to allocate a XXX byte allocation with XXX free bytes and XXXKB until OOM

由于我们崩溃收集系统并没有收集崩溃栈时的memInfo,也没有收集崩溃时的Runtime.getRuntime().maxMemory();所以很难具体定位到是哪个模块申请了过多的内存。这里建议没有自己研发崩溃收集系统的团队考虑接入Fabric,可以收集到崩溃发生时所有的线程栈,而不只是崩溃的线程栈,非常方便定位这种OOM的问题。

问题调查:

秉承没有头绪先看源码的原则,通过Android源码分析,OOM的崩溃是由 runtime/thread.cc内抛出的异常。通过分析相关代码,可以看到,除了常规内存不足外,还有另外一种报OOM的地方,即线程创建失败。

然而创建线程为啥会报OOM的错出来呢?查找了一下相关文档,发现问题大概有两个方向。

方向一:

其中一个方向涉及到Linux下的最大打开文件数问题——Linux系统下,每个进程最大打开文件的数目有一个上限限制。进程每打开一个文件就会产生一个文件描述符fd(记录在 /proc/pid/fd 下面),这个限制表明fd的数目不能超过Max open files规定的数目。

而fd的增加的时机有:

  • 创建socket网络连接。
  • 打开文件。
  • 创建HandlerThread。
  • 创建NIO的Channel(读写各占用一个fd)
top Created with Sketch.