foreword
Two days ago, I wrote an article to briefly discuss the principle of ANR generation. The link is as follows:
[Android] ANR principle analysis
Those who are interested can go to observe and observe. Today's article will write about how to analyze ANR logs.
Causes of ANRs
ANR caused by the application layer (time-consuming operation)
- The main thread takes a long time
- The main thread method executes an infinite loop
- The main thread waits too long for the child thread to release the lock
- The application memory is tight. When an application is in a memory tight state for a long time, it will lead to frequent memory exchange, which will cause some operations of the application to time out.
ANR caused by the system layer
- CPU is preempted: Generally speaking, playing games in the foreground may cause your background broadcast to be preempted by CPU
- System services cannot respond in a timely manner: such as obtaining system contacts, etc., system services are all Binder mechanisms, and service capabilities are limited. It is possible that system services do not respond for a long time and cause ANR
- Large amounts of memory used by other apps
Export and view ANR logs
method one
When ANR occurs in the system, the device will automatically output the ANR log to /data/anr/
the directory , as shown below:
For these files, we can directly double-click to open them on Android Studio.
Method Two
Excuting an order:
adb bugreport D:\mybug\bugrep.zip
You can export all bug logs of the device. After executing the command, you will get a zip file in the specified folder. Unzip the file and open it. The file directory is as follows:
Among them, the anr log of the device will be saved in this path: D:\mybug\bugrep\FS\data\anr
, as shown in the figure:
In addition, the D:\mybug\bugrep\bugreport-device.200216.002-2022-06-16-15-30-10.txt
file also has anr log printing, we can search for some abnormal information of the file through the following keywords, such as:
"main" prio=: search anr related information
beginning of crash: search for crash-related information
CPU usage from: search for cpu usage information
How to analyze ANR logs
An ANR log will contain the usage of all processes in the current device, and each process will start with
----- pid 16808 at date -----
starting with
----- end 16808 -----
ends with
As follows:
----- pid 16808 at 2022-06-16 16:56:04 -----
Cmd line: com.example.demoproject
...
...
...
----- end 16808 -----
In addition, there will be some process memory-related information in each process log , such as:
----- pid 16808 at 2022-06-16 16:56:04 -----
Cmd line: com.example.demoproject
...
...
Total number of allocations 59378 // 进程创建到现在一共创建了多少对象
Total bytes allocated 8815KB // 进程创建到现在一共申请了多少内存
Total bytes freed 6847KB // 进程创建到现在一共释放了多少内存
Free memory 23MB // 空闲内存(可用内存)
Free memory until GC 23MB // GC前的空闲内存
Free memory until OOME 190MB // OOM之前的可用内存,当这个值很小的时候,已经处于内存紧张状态,应用可能占用了过多的内存
Total memory 25MB // 当前总内存(已用+可用)
Max memory 192MB // 进程最多能申请的内存
...
----- end 16808 -----
In addition, there will be process stack information in each process log. The stack information is very important. It shows the current state of all threads in the process where ANR occurs .
----- pid 16808 at 2022-06-16 16:56:04 -----
...
suspend all histogram: Sum: 114us 99% C.I. 2us-27us Avg: 12.666us Max: 27us
DALVIK THREADS (14):
"Signal Catcher" daemon prio=5 tid=7 Runnable
| group="system" sCount=0 dsCount=0 flags=0 obj=0x182c0298 self=0x7914b9c000
| sysTid=16819 nice=0 cgrp=default sched=0/0 handle=0x791a98fd50
| state=R schedstat=( 28572293 3522448 11 ) utm=1 stm=1 core=0 HZ=100
| stack=0x791a899000-0x791a89b000 stackSize=991KB
| held mutexes= "mutator lock"(shared held)
native: #00 pc 00000000004108e8 /apex/com.android.runtime/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+140)
native: #01 pc 00000000004f8040 /apex/com.android.runtime/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+512)
native: #02 pc 000000000051297c /apex/com.android.runtime/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+828)
native: #03 pc 000000000050b7a0 /apex/com.android.runtime/lib64/libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+456)
native: #04 pc 000000000050ac84 /apex/com.android.runtime/lib64/libart.so (art::ThreadList::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool)+1964)
native: #05 pc 000000000050a364 /apex/com.android.runtime/lib64/libart.so (art::ThreadList::DumpForSigQuit(std::__1::basic_ostream<char, std::__1::char_traits<char>>&)+844)
native: #06 pc 00000000004c5778 /apex/com.android.runtime/lib64/libart.so (art::Runtime::DumpForSigQuit(std::__1::basic_ostream<char, std::__1::char_traits<char>>&)+200)
native: #07 pc 00000000004d9bb0 /apex/com.android.runtime/lib64/libart.so (art::SignalCatcher::HandleSigQuit()+1352)
native: #08 pc 00000000004d8c5c /apex/com.android.runtime/lib64/libart.so (art::SignalCatcher::Run(void*)+252)
native: #09 pc 00000000000e68a0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
native: #10 pc 0000000000084b6c /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
(no managed stack frames)
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 dsCount=0 flags=1 obj=0x72e0ee78 self=0x79aafbcc00
| sysTid=16808 nice=-10 cgrp=default sched=0/0 handle=0x79ac524ed0
| state=S schedstat=( 1140726262 41301458 368 ) utm=94 stm=20 core=0 HZ=100
| stack=0x7fe35ed000-0x7fe35ef000 stackSize=8192KB
| held mutexes=
at com.example.demoproject.view.MainActivity.doSomething(MainActivity.kt:51)
- waiting to lock <0x02250ad8> (a com.example.demoproject.view.MainActivity) held by thread 18
at com.example.demoproject.view.MainActivity.click2(MainActivity.kt:36)
at java.lang.reflect.Method.invoke(Native method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:441)
at android.view.View.performClick(View.java:7259)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
at android.view.View.performClickInternal(View.java:7236)
at android.view.View.access$3600(View.java:801)
at android.view.View$PerformClick.run(View.java:27896)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7397)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
"Jit thread pool worker thread 0" daemon prio=5 tid=2 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x182c0220 self=0x7919600000
| sysTid=16814 nice=0 cgrp=default sched=0/0 handle=0x791aa94d40
| state=S schedstat=( 44810570 11604064 76 ) utm=4 stm=0 core=0 HZ=100
| stack=0x791a996000-0x791a998000 stackSize=1023KB
| held mutexes=
kernel: (couldn't read /proc/self/task/16814/stack)
native: #00 pc 000000000008033c /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28)
native: #01 pc 000000000014b1f4 /apex/com.android.runtime/lib64/libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+148)
native: #02 pc 00000000005143dc /apex/com.android.runtime/lib64/libart.so (art::ThreadPool::GetTask(art::Thread*)+256)
native: #03 pc 0000000000513768 /apex/com.android.runtime/lib64/libart.so (art::ThreadPoolWorker::Run()+144)
native: #04 pc 0000000000513228 /apex/com.android.runtime/lib64/libart.so (art::ThreadPoolWorker::Callback(void*)+148)
native: #05 pc 00000000000e68a0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
native: #06 pc 0000000000084b6c /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
(no managed stack frames)
...
----- end 16808 -----
In the screenshot above, there are three threads, one is Signal Catcher
thread , one is main
thread, and one is thread status. Their thread statuses are and status Jit thread pool worker thread 0
respectively .Runnable
blocked
native
In Java, there are 6 thread states (for details, please refer to the article I wrote before for more detailed multithreading and concurrency knowledge: An article to understand multithreading and concurrent programming
), as follows:
- NEW - Creation state
- RUNNABLE - ready or running state
- BLOCKED - blocked state
- WATING - waiting state
- TIMED_WAITING - Timed waiting state
- TERMINATED - Terminated status
So, what is native
the status ?
In fact, this state is the thread state defined in cpp
the code , and its java
relationship with the thread state defined by is as follows:
As can be seen from the above, native
the java thread state corresponding to the state is runnable
the state .
Stack information is the first important information we analyze ANR, generally speaking:
- The main thread is in BLOCK / WAITING / TIMEWAITING state, which is basically anr caused by function blocking
- If the main thread is normal, other factors such as CPU load and memory environment should be checked
In addition, in the anr log, there are some common parameters, and their meanings are as follows:
- group: the thread group in which the thread is located
- sCount: The number of times the thread was suspended normally
- dsCount: The number of times the thread was suspended due to debugging
- nice: thread scheduling priority
- utm: thread scheduling time value in user mode
- stm: the scheduling time value of the thread in the kernel state
- core: The number of the CPU core that last executed this thread
ANR Case Study
Case 1 - Slepping anr
The MainActivity code is as follows:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
/**
* 点击睡眠 10s
*/
fun clickToSleep(view: View) {
Thread.sleep(10_000)
}
}
After clicking the button to sleep for 10s, we swipe left or click the back button to exit MainActivity (because the main thread is sleeping, so it cannot exit successfully at this time), wait for a while, and the system will pop up an ANR popup window.
We export the ANR log and open:
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 dsCount=0 flags=1 obj=0x72e0ee78 self=0x79aafbcc00
| sysTid=16356 nice=-10 cgrp=default sched=0/0 handle=0x79ac524ed0
| state=S schedstat=( 1075827038 33414740 291 ) utm=93 stm=14 core=2 HZ=100
| stack=0x7fe35ed000-0x7fe35ef000 stackSize=8192KB
| held mutexes=
at java.lang.Thread.sleep(Native method)
- sleeping on <0x04fbafa5> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:440)
- locked <0x04fbafa5> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:356)
at com.example.demoproject.view.MainActivity.clickToSleep(MainActivity.kt:57)
at java.lang.reflect.Method.invoke(Native method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:441)
at android.view.View.performClick(View.java:7259)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
at android.view.View.performClickInternal(View.java:7236)
at android.view.View.access$3600(View.java:801)
at android.view.View$PerformClick.run(View.java:27896)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7397)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
It is also easy to find from the stack information that when MainActivity.clickToSleep
the method , the thread sleeps, which eventually leads to anr.
Case 2 - Blocked anr
The MainActivity code is as follows:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
/**
* 点击按钮1 - 创建一个子线程并持有当前 Activity 对象锁然后开始睡眠 10s
*/
fun click1(view: View) {
testBlockThread()
}
/**
* 点击按钮2 - 在主线程中尝试获取 Activity 对象锁并打印 log
*/
fun click2(view: View) {
doSomething()
}
private fun testBlockThread() {
val thread = Thread {
synchronized(this) {
Log.i("testLog", "开始睡眠 10s.. 当前线程名称=${
Thread.currentThread().name} 线程id=${
Thread.currentThread().id}")
Thread.sleep(10_000)
}
}
thread.name = "MyTestBlockThread"
thread.start()
}
private fun doSomething() {
synchronized(this) {
Log.i("testLog", "doSomething.. 当前线程名称=${
Thread.currentThread().name} 线程id=${
Thread.currentThread().id}")
}
}
When button 1 is clicked first, a sub-thread will be created MyTestBlockThread
and hold the current Activity object lock and then sleep for 10s, and then continue to click button 2. At this time, the main thread will try to acquire the Activity object lock and execute it, because the lock is being MyTestBlockThread
held by the sub-thread Yes, therefore, the main thread will be locked block
until the child thread releases the lock.
When the main thread is blocked, we swipe left or click the return button to exit MainActivity (since the main thread is being blocked, it is impossible to exit successfully at this time), wait for a while, and the system will pop up an ANR pop-up window.
Below we export the ANR log and open it:
It can be found that there is a line here:
- waiting to lock <0x02250ad8> (a com.example.demoproject.view.MainActivity) held by thread 18
Its meaning is that the main thread is being blocked, and it is waiting for thread 18 to release the lock, which eventually leads to ANR.
So, who is this thread 18? Let's continue to look at the anr log:
Here, found this line of log:
"MyTestBlockThread" prio=5 tid=18 Sleeping
Its meaning is: the thread MyTestBlockThread
is Sleeping
, it's prio=5 tid=18
.
Case 3 - time consuming or infinite loop method
The MainActivity code is as follows:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun click(view: View) {
doSomething()
}
private fun doSomething() {
while (true) {
Log.i("testLog", "doSomething.. 当前线程名称=${
Thread.currentThread().name} 线程id=${
Thread.currentThread().id}")
}
}
}
When we click the button, the log will be continuously printed in the main thread through an infinite loop, so doSomething()
the method can be considered as a time-consuming method. After clicking the button, we slide left or click the return button to exit MainActivity (because the main thread is busy, So it is impossible to exit successfully at this time), wait for a while, and the system will pop up an ANR pop-up window.
Below we export the ANR log and open it:
----- pid 13231 at 2022-06-17 14:23:41 -----
Cmd line: com.example.demoproject
...
"main" prio=5 tid=1 Runnable
| group="main" sCount=0 dsCount=0 flags=0 obj=0x72b20e78 self=0x77fe5a6c00
| sysTid=13231 nice=-10 cgrp=default sched=0/0 handle=0x77ffb0eed0
| state=R schedstat=( 31694533124 58819622 723 ) utm=1310 stm=1859 core=5 HZ=100
| stack=0x7fdc2b7000-0x7fdc2b9000 stackSize=8192KB
| held mutexes= "mutator lock"(shared held)
native: #00 pc 00000000004108e8 /apex/com.android.runtime/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+140)
native: #01 pc 00000000004f8040 /apex/com.android.runtime/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+512)
native: #02 pc 000000000051297c /apex/com.android.runtime/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+828)
native: #03 pc 00000000004f8d4c /apex/com.android.runtime/lib64/libart.so (art::Thread::RunCheckpointFunction()+176)
native: #04 pc 00000000003713fc /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::CheckJNI::ReleaseStringCharsInternal(char const*, _JNIEnv*, _jstring*, void const*, bool, bool)+1356)
native: #05 pc 00000000001507cc /system/lib64/libandroid_runtime.so (android::android_util_Log_println_native(_JNIEnv*, _jobject*, int, int, _jstring*, _jstring*)+232)
at android.util.Log.println_native(Native method)
at android.util.Log.i(Log.java:176)
at com.example.demoproject.view.MainActivity.doSomething(MainActivity.kt:52)
at com.example.demoproject.view.MainActivity.click2(MainActivity.kt:36)
at java.lang.reflect.Method.invoke(Native method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:441)
at android.view.View.performClick(View.java:7259)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
at android.view.View.performClickInternal(View.java:7236)
at android.view.View.access$3600(View.java:801)
at android.view.View$PerformClick.run(View.java:27896)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7397)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
...
----- end 16808 -----
It is found that the main thread is in Runnable
the state , not in the idle state, and there is a key log line in the stack information:
at com.example.demoproject.view.MainActivity.doSomething(MainActivity.kt:52)
This means that the anr caused by doSomething
this .
Case 4 - normal situation
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x72e0ee78 self=0x79aafbcc00
| sysTid=1496 nice=-2 cgrp=default sched=0/0 handle=0x79ac524ed0
| state=S schedstat=( 50585681414 30364690662 64096 ) utm=3092 stm=1966 core=2 HZ=100
| stack=0x7fe35ed000-0x7fe35ef000 stackSize=8192KB
| held mutexes=
kernel: (couldn't read /proc/self/task/1496/stack)
native: #00 pc 00000000000d0f58 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
native: #01 pc 00000000000180bc /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
native: #02 pc 0000000000017f8c /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+56)
native: #03 pc 000000000013b8f4 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:336)
at android.os.Looper.loop(Looper.java:174)
at com.android.server.SystemServer.run(SystemServer.java:546)
at com.android.server.SystemServer.main(SystemServer.java:354)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:913)
The above main thread stack is a normal idle stack, indicating that the main thread is waiting for new messages. If the main thread in the ANR log is in such a state, there may be two reasons for anr:
- The anr is caused by other factors such as cpu resource preemption or memory shortage
- When this anr log is captured, the main thread has returned to normal
Case 5 - CPU is preempted by other applications
When no effective information can be found in the anr log, in this case we have to look at the cpu information,
This log can generally be viewed in bugreport.txt
the file . For details on how to export bugreport.txt
the file, see the steps in "Exporting and Viewing ANR Logs" in this article.
Take out the cpu information log as follows:
-------------------------------------------------------------------------------
DUMP OF SERVICE CRITICAL cpuinfo:
Load: 5.28 / 5.71 / 5.58
CPU usage from 275243ms to 190078ms ago (2022-06-16 15:25:36.158 to 2022-06-16 15:27:01.323):
51% 695/audioserver: 35% user + 15% kernel / faults: 3071 minor
17% 1496/system_server: 9% user + 8% kernel / faults: 43598 minor
14% 2077/VosRXThread: 0% user + 14% kernel
3.1% 666/[email protected]: 0.8% user + 2.3% kernel / faults: 170 minor
3.1% 642/[email protected]: 0.2% user + 2.8% kernel / faults: 2 minor
...................................省略 N 行...........................................
21% TOTAL: 6.4% user + 10% kernel + 0.1% iowait + 2.4% irq + 1.4% softirq
--------- 0.007s was the duration of dumpsys cpuinfo, ending at: 2022-06-16 15:30:11
-------------------------------------------------------------------------------
How to analyze this log? The meaning of this part of it is as follows:
1、Load: 5.28 / 5.71 / 5.58 // 代表了设备在 1、5、15 分钟内 正在使用和等待使用CPU 的活动进程的平均数
2、CPU usage from 275243ms to 190078ms ago (2022-06-16 15:25:36.158 to 2022-06-16 15:27:01.323) // 表明负载信息抓取是在 275243ms ~ 190078ms之间的,且时间点是从 2022-06-16 15:25:36.158 开始
3、中间打印百分比的部分 // 各个进程占用的CPU的详细情况
4、最后一行 // 各个进程合计占用的CPU信息。
There are also some nouns and their meanings as follows:
1、user: 用户态
2、kernel: 内核态
3、faults: 内存缺页,minor —— 轻微的,major —— 重度,需要从磁盘拿数据
4、iowait: IO 等待占比,如果占比很高,意味着有很大可能是io耗时导致ANR
5、irq: 硬中断,
6、softirq: 软中断
Note the following two points:
- If the proportion of owait is high, it means that there is a high possibility of ANR caused by io time-consuming. Specifically, check whether there are more process faults major
- The load of a single-process CPU is not limited to 100%, but several hundred percent if there are several cores. For example, if there are 4 cores, the upper limit is 400%.
After understanding how to view the cpu log, let's go back to the specific log to view,
It is found that the process ranked first above audioserver
is the process with the highest CPU usage in the system, reaching 51%, which is within the normal CPU usage range. Therefore, no valid log that causes anr is found in this log.
If it is found that the process ranked first takes up extremely high cpu resources, then it is very likely that this process caused the anr.
Case 6 - System service timed out
If the system service times out, the log will generally contain BinderProxy.transactNative
keywords , as follows:
It can be seen from the stack getActiveNetworkInfo
that the method ANR occurred. We know that the system services are all Binder mechanisms. Binder has a total of 16 threads, and the service capability is limited, so it is possible that the system service does not respond for a long time and causes ANR.
If other applications occupy the Binder thread, the current application can only wait, and you can search further blockUntilThreadAvailable
keywords
at android.os.Binder.blockUntilThreadAvailable(Native method)
If you find that the stack of a certain thread contains this word, you can further look at its stack to determine what system service it calls.
This type of ANR is a problem of the system environment. If this problem occurs frequently on a certain type of machine, the application layer can consider an avoidance strategy.
Case 7 - tight memory
If the CPU and stack in the log are normal, but ANR still occurs, you can further consider whether the anr is caused by memory shortage.
In bugreport.txt
the file , we can also am_meminfo
search the logs by the following keywords:
06-16 02:14:42.014 1000 1496 1575 I am_meminfo: [1163550720,172752896,7536640,272494592,512559104]
The five values in the array refer to the
- Cached
- Free
- Zram
- Kernel
- Native
Among them, Cached + Free
represents the current entire mobile phone 可用内存
. If the value is small, it means that the memory is in a tight state .
Generally, the judgment threshold for low memory is: the threshold below 4G memory mobile phone: 350MB, and the threshold above: 450MB
In addition, if no logs can be found by am_meminfo
searching , then we can onTrimMemory
search by , such as:
10-31 22:37:33.458 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
It can also be used as a reference to judge the memory shortage. It can be seen that here level 为 80
, let's see how this level is defined in Android:
/**
* 进程接近后台 LRU 列表的末尾,如果没有很快找到更多内存,进程将被杀死
*/
static final int TRIM_MEMORY_COMPLETE = 80;
/**
* 进程在后台 LRU 列表的中间;释放内存可以帮助系统保持列表中稍后运行的其他进程,以获得更好的整体性能
*/
static final int TRIM_MEMORY_MODERATE = 60;
/**
* 进程已进入 LRU 列表。这是一个清理资源的好机会,如果用户返回应用程序,这些资源可以高效快速地重新构建
*/
static final int TRIM_MEMORY_BACKGROUND = 40;
/**
* 进程已显示用户界面,现在不再显示。此时应释放 UI 的大量分配,以便更好地管理内存
*/
static final int TRIM_MEMORY_UI_HIDDEN = 20;
/**
* 该进程不是可消耗的后台进程,但设备运行的内存极低,即将无法保持任何后台进程运行。您正在运行的进程应尽可能多地释放非关键资源,以允许该内存在其他地方使用。在此之后将发生的下一件事是调用 onLowMemory() 以报告在后台根本无法保留任何内容,这种情况可能会开始显着影响用户
*/
static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;
/**
* 进程不是可消耗的后台进程,但设备内存不足。您正在运行的进程应该释放不需要的资源,以允许在其他地方使用该内存
*/
static final int TRIM_MEMORY_RUNNING_LOW = 10;
/**
* 进程不是可消耗的后台进程,但设备运行的内存适中。您正在运行的进程可能希望释放一些不需要的资源以供其他地方使用
*/
static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
It can be seen from this that 80 is a very serious level. If more memory is not found soon, the process will be killed, and it can be seen from the log that this process is a launcher process, and even the desktop is about to be killed. Ordinary apps are certainly not much better.
Generally speaking, the memory of the device is tight, which will cause ANR to occur in multiple applications.
reference article
Dry goods: comprehensive analysis of ANR log analysis (including classic stack examples)