体现APP稳定性的重要数据就是Crash率,众多crash种最棘手最难定位的是OOM问题。APP中所有的内存泄漏都会慢慢累积在内存中,最有就容易导致OOM。
Android中OOM类型
Android虚拟机最终抛出OutOfMemoryError代码在/art/runtime/thread.cc
void Thread::ThrowOutOfMemoryError(const char* msg)
参数 msg 携带了 OOM 时的错误信息
堆内存分配失败
系统源码在/art/runtime/gc/heap.cc
void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type)
抛出时的错误信息:
oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free << " free bytes and " << PrettySize(GetFreeMemoryUntilOOME()) << " until OOM";
也可以分为不同的两种类型:
1、为对象分配内存时达到进程的内存上限。有Runtime.getRuntime.MaxMemory()可以得到Android中每个进程被系统分配的内存上限,当进程占用内存达到这个上限就会发生OOM。
2、没有足够大小的连续地址空间。进程中存在大量的内存碎片导致的,堆栈信息比第一种OOM堆栈多出一段信息:
failed due to fragmentation (required continguous free "<< required_bytes << " bytes for a new buffer where largest contiguous free " << largest_continuous_free_pages << " bytes)"; 其详细代码在art/runtime/gc/allocator/rosalloc.cc中,这里不作详述。
创建线程失败
系统源码:/art/runtime/thread.cc
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon)
抛出时的错误信息:
"Could not allocate JNI Env"
或者
StringPrintf("pthread_create (%s stack) failed: %s", PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
Android创建线程,中两个关键节点创建JNIEnv结构体和创建线程,均有可能抛出OOM。
创建JNI失败
创建JNIEnv可以归为两个步骤:
通过Android的匿名共享内存分配4KB一个page内核态内存。
再通过Linux的mmap调用映射到用户态虚拟内存地址空间。
第一步创建共享内存时,需要打开/dev/ashmem文件,所以需要一个FD(文件描述符)。此时,如果创建FD数已经达到上限,会导致创建JNIEnv失败,抛出错误:
E/art: ashmem_create_region failed for 'indirect ref table': Too many open files
java.lang.OutOfMemoryError: Could not allocate JNI Env
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:730)
第二步调用mmap时,如果进程虚拟内存地址空间耗尽会导致JNIEnv失败,抛出异常信息
E/art: Failed anonymous mmap(0x0, 8192, 0x3, 0x2, 116, 0): Operation not permitted. See process maps in the log.
java.lang.OutOfMemoryError: Could not allocate JNI Env
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:1063)
分析清楚OOM问题的原因之后,我们对于线上的问题
网友评论