序文
2 日前に、ANR 生成の原理について簡単に説明する記事を書きました. リンクは次のとおりです。
興味のある方は見学・見学に行ってみてください 今日の記事はANRログの解析方法について書いていきます。
ANR の原因
アプリケーション層に起因するANR(手間のかかる運用)
- メインスレッドに時間がかかる
- メインスレッドメソッドは無限ループを実行します
- 子スレッドがロックを解放するのをメイン スレッドが長時間待機している
- アプリケーションのメモリが不足している. アプリケーションが長時間メモリ不足の状態にあると、メモリ交換が頻繁に発生し、アプリケーションの一部の操作がタイムアウトします.
システムレイヤーによるANR
- CPU がプリエンプトされる: 一般的に言えば、フォアグラウンドでゲームをプレイすると、バックグラウンド ブロードキャストが CPU によってプリエンプトされる可能性があります。
- システム サービスがタイムリーに応答できない: システム連絡先の取得など、システム サービスはすべて Binder メカニズムであり、サービス機能が制限されている. システム サービスが長時間応答せず、ANR が発生する可能性がある.
- 他のアプリが使用する大量のメモリ
ANR ログのエクスポートと表示
方法1
システムで ANR が発生すると、デバイスは次のように ANR ログを/data/anr/
ディレクトリ。
これらのファイルは、直接ダブルクリックして Android Studio で開くことができます。
方法 2
注文の実行:
adb bugreport D:\mybug\bugrep.zip
デバイスのすべてのバグ ログをエクスポートできます。コマンドを実行すると、指定したフォルダに zip ファイルが生成されます。ファイルを解凍して開きます。ファイル ディレクトリは次のとおりです。
D:\mybug\bugrep\FS\data\anr
その中で、図に示すように、デバイスの anr ログは次のパスに保存されます。
さらに、D:\mybug\bugrep\bugreport-device.200216.002-2022-06-16-15-30-10.txt
ファイルにはログ出力もあり、次のようなキーワードでファイルの異常な情報を検索できます。
"main" prio=: 関連情報を検索
クラッシュの始まり: クラッシュ関連の情報を検索します
CPU 使用率: CPU 使用率情報を検索
ANR ログの分析方法
ANR ログには、現在のデバイス内のすべてのプロセスの使用状況が含まれ、各プロセスは
----- pid 16808 at date -----
で始まります
----- end 16808 -----
で終わる
次のように:
----- pid 16808 at 2022-06-16 16:56:04 -----
Cmd line: com.example.demoproject
...
...
...
----- end 16808 -----
さらに、各プロセス ログには、次のようなプロセスメモリ関連の情報が含まれます。
----- 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 -----
さらに、各プロセス ログにはプロセス スタック情報があります. スタック情報は非常に重要です. ANR が発生したプロセスのすべてのスレッドの現在の状態を示します.
----- 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 -----
上のスクリーンショットには、3 つのスレッドがあり、1 つはSignal Catcher
thread 、1 つはmain
thread 、もう 1 つは thread status です。これらのスレッド ステータスはJit thread pool worker thread 0
、それぞれ およびRunnable
statusですblocked
。native
Java には、次の 6 つのスレッド状態があります (詳細については、以前に書いた、マルチスレッドと同時実行に関する詳しい知識についての記事: マルチスレッドと並行プログラミングを理解するための記事
を参照してください)。
- NEW - 作成状態
- RUNNABLE - 準備完了または実行中の状態
- BLOCKED - ブロック状態
- WATING - 待機状態
- TIMED_WAITING - 時限待機状態
- TERMINATED - 終了ステータス
では、上記のログのnative
ステータス?
実際、この状態はcpp
code、java
で定義されたスレッド状態との関係は次のとおりです。
上記からわかるように、native
状態に対応する Java スレッドの状態はrunnable
state。
スタック情報は、一般的に言えば、ANR を分析する最初の重要な情報です。
- メインスレッドは BLOCK / WAITING / TIMEWAITING 状態にあり、これは基本的に関数のブロックによって引き起こされる anr です
- メインスレッドが正常な場合、CPU 負荷やメモリ環境などの他の要因を確認する必要があります
さらに、anr ログにはいくつかの共通パラメーターがあり、その意味は次のとおりです。
- group: スレッドが配置されているスレッド グループ
- sCount: スレッドが正常に中断された回数
- dsCount: デバッグのためにスレッドが中断された回数
- nice: スレッドのスケジューリング優先度
- utm: ユーザーモードでのスレッドスケジューリング時間値
- stm: カーネル モードでのスレッドのスケジューリング時間の値
- core: このスレッドを最後に実行した CPU コアの番号
ANR のケーススタディ
ケース 1 - 寝ている
MainActivity コードは次のとおりです。
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)
}
}
ボタンをクリックして 10 秒間スリープさせた後、左にスワイプするか、[戻る] ボタンをクリックして MainActivity を終了し (メイン スレッドがスリープしているため、この時点では正常に終了できません)、しばらく待つと、システムがポップアップします。 ANR ポップアップ ウィンドウ。
ANR ログをエクスポートして開きます。
"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)
MainActivity.clickToSleep
メソッドが実行されるとスレッドがスリープし、最終的に anr につながることも、スタック情報から簡単に見つけることができます。
ケース 2 - ブロックされた anr
MainActivity コードは次のとおりです。
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}")
}
}
ボタン 1 が最初にクリックされると、サブスレッドが作成されMyTestBlockThread
、現在の Activity オブジェクト ロックを保持し、10 秒間スリープしてから、ボタン 2 をクリックし続けます。このとき、メイン スレッドは Activity オブジェクト ロックを取得しようとします。MyTestBlockThread
サブスレッドがロックを保持しているため、実行します はい、したがって、子スレッドがロックを解放するblock
まで。
メイン スレッドがブロックされている場合は、左にスワイプするか、戻るボタンをクリックして MainActivity を終了し (メイン スレッドがブロックされているため、現時点では正常に終了できません)、しばらく待つと、システムがポップアップします。 ANR ポップアップ ウィンドウ。
以下では、ANR ログをエクスポートして開きます。
ここに次の行があることがわかります。
- waiting to lock <0x02250ad8> (a com.example.demoproject.view.MainActivity) held by thread 18
これは、メイン スレッドがブロックされており、スレッド 18 がロックを解放するのを待っていることを意味し、最終的に ANR につながります。
で、このスレ18は誰?引き続き anr ログを見てみましょう。
ここで、次のログ行が見つかりました。
"MyTestBlockThread" prio=5 tid=18 Sleeping
その意味は次のとおりです。スレッドMyTestBlockThread
は ですSleeping
、それは ですprio=5 tid=18
。
ケース 3 - 時間がかかる、または無限ループの方法
MainActivity コードは次のとおりです。
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}")
}
}
}
ボタンをクリックすると、ログはメイン スレッドに無限ループを介して連続的に出力されるため、doSomething()
このメソッドは時間のかかるメソッドと見なすことができます. ボタンをクリックした後、左にスライドするか、戻るボタンをクリックして MainActivity を終了します。 (メイン スレッドがビジーなため、現時点では正常に終了できません)、しばらく待つと、システムが ANR ポップアップ ウィンドウを表示します。
以下では、ANR ログをエクスポートして開きます。
----- 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 -----
メイン スレッドがアイドル状態ではなくRunnable
状態、スタック情報に重要なログ行があります。
at com.example.demoproject.view.MainActivity.doSomething(MainActivity.kt:52)
これは、doSomething
この。
ケース 4 - 通常の状況
"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)
上記のメイン スレッド スタックは通常のアイドル スタックであり、メイン スレッドが新しいメッセージを待機していることを示しています。ANR ログのメイン スレッドがこのような状態にある場合、anr には次の 2 つの理由が考えられます。
- anr は、CPU リソースのプリエンプションやメモリ不足などの他の要因によって引き起こされます
- この anr ログがキャプチャされると、メイン スレッドは正常に戻りました
ケース 5 - CPU が他のアプリケーションによってプリエンプトされる
anr ログに有効な情報が見つからない場合、この場合は CPU 情報を調べる必要があります。
このログは通常、bugreport.txt
ファイル.bugreport.txt
ファイルをエクスポートする方法の詳細については、この記事の「ANR ログのエクスポートと表示」の手順を参照してください。
次のように cpu 情報ログを取り出します。
-------------------------------------------------------------------------------
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
-------------------------------------------------------------------------------
このログを分析する方法は? この部分の意味は次のとおりです。
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信息。
また、次のような名詞とその意味もあります。
1、user: 用户态
2、kernel: 内核态
3、faults: 内存缺页,minor —— 轻微的,major —— 重度,需要从磁盘拿数据
4、iowait: IO 等待占比,如果占比很高,意味着有很大可能是io耗时导致ANR
5、irq: 硬中断,
6、softirq: 软中断
次の 2 点に注意してください。
- owait の割合が高い場合は、io 時間のかかることによる ANR の可能性が高いことを意味します 具体的には、メジャーなプロセス障害がより多くないかどうかを確認
- 1 プロセスの CPU の負荷は 100% に制限されるのではなく、複数のコアがある場合は数百パーセント、たとえば 4 コアの場合、上限は 400% です。
cpu ログの表示方法を理解したら、特定のログに戻って表示します。
上位1位のプロセスがシステム内で最もCPU使用率が高いプロセスaudioserver
であること。
最初にランク付けされたプロセスが非常に高い CPU リソースを占有していることが判明した場合、このプロセスが anr の原因である可能性が非常に高くなります。
ケース 6 - システム サービスのタイムアウト
システム サービスがタイムアウトした場合、通常、ログには次のようなBinderProxy.transactNative
キーワードが。
getActiveNetworkInfo
メソッド ANR が発生したことがスタックからわかります。システム サービスはすべて Binder メカニズムであることがわかっています.Binder には合計 16 のスレッドがあり、サービス機能が制限されているため、システム サービスが長時間応答せず、ANR が発生する可能性があります。
他のアプリケーションが Binder スレッドを占有している場合、現在のアプリケーションは待つことしかできず、さらにblockUntilThreadAvailable
キーワードを
at android.os.Binder.blockUntilThreadAvailable(Native method)
特定のスレッドのスタックにこの単語が含まれていることがわかった場合は、そのスタックをさらに調べて、それが呼び出すシステム サービスを特定できます。
このタイプの ANR はシステム環境の問題であり、特定のタイプのマシンでこの問題が頻繁に発生する場合、アプリケーション層は回避策を検討できます。
ケース 7 - タイトなメモリ
ログの CPU とスタックが正常であるにもかかわらず、ANR が引き続き発生する場合は、メモリ不足が原因であるかどうかをさらに検討できます。
bugreport.txt
ファイルでは、am_meminfo
次のキーワードでログを検索することもできます。
06-16 02:14:42.014 1000 1496 1575 I am_meminfo: [1163550720,172752896,7536640,272494592,512559104]
配列内の 5 つの値は、
- キャッシュ済み
- 無料
- ズラム
- カーネル
- ネイティブ
このうち、 は現在の携帯電話全体Cached + Free
を表し可用内存
、値が小さいほどメモリが逼迫した状態であることを意味します。
一般的に、メモリ不足の判定閾値は、4Gメモリ携帯電話未満の閾値:350MB、それ以上の閾値:450MBです。
さらに、キーワードをam_meminfo
検索し、onTrimMemory
次のように検索できます。
10-31 22:37:33.458 20733 20733 E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
メモリ不足を判断するための参考としても使用できます.ここでlevel 为 80
、このレベルが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;
このことから、80 は非常に深刻なレベルであることがわかります。メモリがすぐに見つからない場合、プロセスは強制終了されます。このプロセスはランチャー プロセスであることがログからわかります。通常のアプリは確かにそれほど優れているわけではありません。
一般的に言えば、デバイスのメモリが不足しているため、複数のアプリケーションで ANR が発生します。