二、OOM問(wèn)題的可能原因

重點(diǎn)關(guān)注下面兩點(diǎn)
?? 堆內(nèi)存分配失敗時(shí)的OOM? ==?? /art/runtime/gc/heap.cc
?? 創(chuàng)建線程失敗時(shí)的OOM ? ? ==?? /art/runtime/thread.cc
三、OOM -- 堆內(nèi)存分配失敗
在source code中我們可以看到,當(dāng)堆內(nèi)存分配失敗時(shí),會(huì)拋出一些典型的log,如下代碼

在出現(xiàn)OOM問(wèn)題時(shí),logcat中應(yīng)該會(huì)看到類似下面的信息輸出
08-1911:34:53.8602802828028EAndroidRuntime:java.lang.OutOfMemoryError:Failedtoallocatea20971536byteallocationwith6147912free bytes and6003KB until OOM,target footprint134217728,growth limit134217728
上面這段logcat的大概解釋:想要去分配 20971536 bytes的heap memory,但時(shí)app剩余可用的free heap只有6147912 bytes,而且當(dāng)前app最大可分配的heap是134217728 bytes
堆內(nèi)存分配失敗的原因可以分兩種情況:
? ? ? 1. 超過(guò)APP進(jìn)程的heap內(nèi)存上限 與 2. 沒(méi)有足夠大小的連續(xù)地址空間
3.1 超過(guò)APP進(jìn)程的內(nèi)存上限
Android設(shè)備上java虛擬機(jī)對(duì)單個(gè)應(yīng)用的最大內(nèi)存分配做了約束,超出這個(gè)值就會(huì)OOM。由Runtime.getRuntime.MaxMemory()可以得到Android中每個(gè)進(jìn)程被系統(tǒng)分配的內(nèi)存上限,當(dāng)進(jìn)程占用內(nèi)存達(dá)到這個(gè)上限時(shí)就會(huì)發(fā)生OOM,這也是Android中最常見(jiàn)的OOM類型。
3.2 沒(méi)有足夠大小的連續(xù)地址空間
這種情況一般是進(jìn)程中存在大量的內(nèi)存碎片導(dǎo)致的,其堆棧信息會(huì)比第一種OOM堆棧多出一段類似如下格式的信息
:failed duetofragmentation(required continguous free “<<required_bytes<<“ bytesforanewbuffer where largest contiguous free ”<<largest_continuous_free_pages<<“ bytes)”
相關(guān)的代碼在art/runtime/gc/allocator/rosalloc.cc中,如下
voidRosAlloc::LogFragmentationAllocFailure(std::ostream&os,size_t failed_alloc_bytes){...if(required_bytes>largest_continuous_free_pages){os<<"; failed due to fragmentation ("<<"required contiguous free "<<required_bytes<<" bytes"<<new_buffer_msg<<", largest contiguous free "<<largest_continuous_free_pages<<" bytes"<<", total free pages "<<total_free<<" bytes"<<", space footprint "<<footprint_<<" bytes"<<", space max capacity "<<max_capacity_<<" bytes"<<")"<<std::endl;}}
這種場(chǎng)景比較難模擬,這里就不做演示了。
四、OOM -- 創(chuàng)建線程失敗
Android中線程(Thread)的創(chuàng)建及內(nèi)存分配過(guò)程分析可以參見(jiàn)如下這篇文章:https://blog.csdn.net/u011578734/article/details/109331764
線程創(chuàng)建會(huì)消耗大量的系統(tǒng)資源(例如內(nèi)存),創(chuàng)建過(guò)程涉及java層和native的處理。實(shí)質(zhì)工作是在native層完成的,相關(guān)代碼位于 /art/runtime/thread.cc

4.1 創(chuàng)建JNI Env 失敗
一般有兩種原因
1. FD溢出導(dǎo)致JNIEnv創(chuàng)建失敗了,一般logcat中可以看到信息?Too many open files ... Could not allocate JNI Env
當(dāng)進(jìn)程fd數(shù)(可以通過(guò) ls /proc/pid/fd | wc -l 獲得)突破 /proc/pid/limits中規(guī)定的Max open files時(shí),產(chǎn)生OOM
E/art:ashmem_create_region failedfor'indirect ref table':Toomanyopenfilesjava.lang.OutOfMemoryError:Couldnot allocate JNIEnvatjava.lang.Thread.nativeCreate(NativeMethod)atjava.lang.Thread.start(Thread.java:730)
2. 虛擬內(nèi)存不足導(dǎo)致JNIEnv創(chuàng)建失敗了,一般logcat中可以看到信息?Could not allocate JNI Env: Failed anonymous mmap

4.2 創(chuàng)建線程失敗
一般有兩種原因
1. 虛擬內(nèi)存不足導(dǎo)致失敗,一般logcat中可以看到信息?mapped space: Out of memory? ... pthread_create (1040KB stack) failed: Out of memory
native層通過(guò)FixStackSize設(shè)置線程棧大小,默認(rèn)情況下,線程棧所需內(nèi)存總大小 = 1M + 8k + 8k,即為1040k。

4.3 debug技巧
對(duì)于FD的限制
可以執(zhí)行?cat /proc/pid/limits來(lái)查看Max open files 最大打開(kāi)的文件數(shù)量
可以執(zhí)行?ls /proc/pid/fd | wc -l來(lái)查看進(jìn)程打開(kāi)的文件數(shù)量
對(duì)于線程數(shù)量的限制
可以執(zhí)行cat /proc/sys/kernel/threads-max?查看系統(tǒng)最多可以創(chuàng)建多少線程
可以執(zhí)行echo 3000 > /proc/sys/kernel/threads-max修改這個(gè)值,做測(cè)試
查看系統(tǒng)當(dāng)前的線程數(shù)?top -H
對(duì)于虛擬內(nèi)存使用情況
可以執(zhí)行?cat /proc/pid/status | grep Vm查看VmSize及VmPeak