近期,着手分析游戏的OOM问题,该问题在bugly上的量级,恐怖吓人的百万级,处于java 异常的top 1, 如下所示:
发生的设备,基本上都是32位的cpu架构
分析过程
先来看下报错的堆栈,基本上都是发生在创建线程的环节:
单纯从java 报错的调用栈来看,往往会简单的认为是app 线程过多导致OOM。然而,事实真的是如此嘛?
先通过adb shell 来,查询下游戏的线程数
经历游戏的主流程后,发现线程数并没有增加特别多,普遍处于200多上下,没有到达厂商限制最大线程数(华为 设备上限制500+的线程数)。
从而推断出,并不是线程数量过多导致OOM的问题。
接着继续探究,OOM 是内存不足时,再次申请开辟内存失败,从而抛出的异常。
去查看下会不会是系统内存不足,导致的元凶?
从bugly上统计的数据来看,游戏发生OOM时,系统可用内存还是很充足的,因此也排除了这个原因。
接着继续,分析看下是不是Dalivk Heap内存不足?
java heap OOM异常,会带着,Failed to allocate a 294920 byte allocation with 147040 free bytes and 143KB until OOM
,与报错堆栈上的pthread_create (1040KB stack) failed: Try again
完全对不上,因此也不是dalvik 堆内存不足导致的。
排除了三个可能性后,结合32位设备,推断唯一剩下的便是app 的虚拟内存不足。
更多有关于内存情况,请阅读Android 内存分析(java/native heap内存、虚拟内存、处理器内存 )和Android adb shell 查看App内存(java堆内存/vss虚拟内存/详细的内存状况/内存快照hprof)和系统可用内存
32位的进程最大虚拟内存是4G,其中内核空间占1G +用户空间占3G,存在限制。
猜想验证
编译一个游戏渠道包,找个32位的设备,安装后。
登录游戏,进入游戏大厅,查看下虚拟内存情况:
发现虚拟内存Vmsize: 2.9G ,处于正常值。
接着玩几个比较大型的游戏地图:
再次查看虚拟内存:
虚拟内存,直线飙升,到3.4G
尝试继续玩不同的地图,也随着记录app的虚拟内存
虚拟内存马上接近4G时,线程数任然是192个正常范围,游戏出现黑屏,接着突然闪退。
这时的logcat 输出,线程oom的日志:
2023-06-12 18:29:23.950 23936-24275/? E/Adreno-GSL: <gsl_memory_alloc_pure:2615>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
2023-06-12 18:29:23.951 23936-24275/? E/Adreno-GSL: <gsl_memory_alloc_pure:2615>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
2023-06-12 18:29:23.953 23936-24275/? E/Adreno-GSL: <gsl_memory_alloc_pure:2615>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
2023-06-12 18:29:23.953 23936-24016/? E/AndroidRuntime: FATAL EXCEPTION: miniCrashTask
Process: com.minitech.miniworld.meta, PID: 23936
java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:884)
at okio.AsyncTimeout.scheduleTimeout(AsyncTimeout.java:88)
at okio.AsyncTimeout.enter(AsyncTimeout.java:80)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:235)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:354)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:226)
at okhttp3.internal.http1.Http1Codec.readHeaderLine(Http1Codec.java:215)
at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
最终,验证了猜想,是32位的app进程存在虚拟内存4G限制,才是导致OOM的罪魁祸首。
总结:
在32位的设备上或者沙盒环境(不支持64位cpu)的环境下,游戏很容易发生OOM问题。