概述
生成trace文件
当APP(包括系统APP和用户APP)进程出现ANR、应用响应慢或WatchDog的监视没有得到回馈时,除了logcat生成的log外还可以在系统指定目录下找到traces文件进行分析。系统会dump此时的top进程,进程中Thread的运行状态就都dump到这个Trace文件中了。系统生成的Trace文件保存在data/anr,可以用过命令adb pull data/anr/取出。
另外在bugreport文件中也存在trace文件,位于FS/data/anr/目录下。
trace参数解析
trace文件结构
(1) 进程开始
一个trace文件内包含多个进程,每个进程的开头为:
----- pid XXXX at date time -----
如:
----- pid 2297 at 2021-11-24 11:07:11.451805500+0800 -----
(2) 进程相关信息
接着是进程的一些信息,如:
Cmd line: 进程名
Build fingerprint: ......
ABI: 'arm64'
Build type: optimized
......
(3) 线程的堆栈信息
接下来是该进程的每个线程的堆栈信息,以“DALVIK THREADS (xxx)”开始,xxx表示线程数量。
(4) Waiting Channels信息
列出了线程在内核中的状态。
如下示例:
----- Waiting Channels: pid 进程号 at 日期 时间 -----
Cmd line: 进程名
sysTid=线程号 线程在内核中的状态
sysTid=4147 binder_wait_for_work
sysTid=4157 0
sysTid=4158 pipe_read
sysTid=4159 futex_wait_queue_me
......
sysTid=4162 futex_wait_queue_me
sysTid=4164 binder_wait_for_work
sysTid=4227 do_epoll_wait
trace-进程头部参数
//进程pid以及时间
----- pid 2297 at 2021-11-24 11:07:11.451805500+0800 -----
//进程名
Cmd line: system_server
Build fingerprint: 'Xiaomi/cupid/cupid:12/SKQ1.211006.001/V13.0.0.1119.SLCCNXM:user/release-keys'
ABI: 'arm64'
Build type: optimized
Zygote loaded classes=19807 post zygote classes=12398
//未知
Dumping registered class loaders
#0 dalvik.system.PathClassLoader: [], parent #1
//省略......
#13 dalvik.system.PathClassLoader: [/apex/com.android.tethering/priv-app/InProcessTethering/InProcessTethering.apk], parent #1
Done dumping class loaders
Classes initialized: 0 in 0
Intern table: 65084 strong; 8458 weak
JNI: CheckJNI is off; globals=4954 (plus 2383 weak)
//加载的动态库以及数量
Libraries:/system/lib64/libalarm_jni.so ...... libwebviewchromium_loader.so (30)
//分配堆内存大小123MB,其中100M已用,总分配1323875个对象
Heap: 0% free, 100MB/123MB; 1323875 objects
//以下为GC相关信息,未知
Dumping cumulative Gc timings
Start Dumping Averages for 240 iterations for concurrent copying
MarkingPhase: Sum: 47.808s Avg: 199.201ms
ScanCardsForSpace: Sum: 19.013s Avg: 79.223ms
ProcessMarkStack: Sum: 13.235s Avg: 55.148ms
SweepSystemWeaks: Sum: 7.349s Avg: 30.624ms
ScanImmuneSpaces: Sum: 7.107s Avg: 29.614ms
ClearFromSpace: Sum: 4.578s Avg: 19.077ms
VisitConcurrentRoots: Sum: 1.950s Avg: 8.125ms
CaptureThreadRootsForMarking: Sum: 1.429s Avg: 5.956ms
CopyingPhase: Sum: 1.207s Avg: 5.032ms
ProcessReferences: Sum: 603.525ms Avg: 2.514ms
FlipOtherThreads: Sum: 560.643ms Avg: 2.336ms
ForwardSoftReferences: Sum: 542.155ms Avg: 2.258ms
ThreadListFlip: Sum: 533.359ms Avg: 2.222ms
VisitNonThreadRoots: Sum: 330.042ms Avg: 1.375ms
ReclaimPhase: Sum: 319.684ms Avg: 1.332ms
EnqueueFinalizerReferences: Sum: 283.832ms Avg: 1.182ms
FlipThreadRoots: Sum: 274.825ms Avg: 1.145ms
GrayAllDirtyImmuneObjects: Sum: 198.667ms Avg: 827.779us
InitializePhase: Sum: 143.778ms Avg: 599.075us
SweepLargeObjects: Sum: 140.389ms Avg: 584.954us
EmptyRBMarkBitStack: Sum: 89.826ms Avg: 374.275us
ResumeOtherThreads: Sum: 34.853ms Avg: 145.220us
RecordFree: Sum: 27.329ms Avg: 113.870us
SweepAllocSpace: Sum: 18.205ms Avg: 75.854us
ResumeRunnableThreads: Sum: 15.817ms Avg: 65.904us
MarkStackAsLive: Sum: 14.486ms Avg: 60.358us
(Paused)ClearCards: Sum: 12.042ms Avg: 50.175us
(Paused)GrayAllNewlyDirtyImmuneObjects: Sum: 10.684ms Avg: 44.516us
MarkZygoteLargeObjects: Sum: 8.941ms Avg: 37.254us
SwapBitmaps: Sum: 4.385ms Avg: 18.270us
Sweep: Sum: 2.990ms Avg: 12.458us
(Paused)FlipCallback: Sum: 2.888ms Avg: 12.033us
(Paused)SetFromSpace: Sum: 2.376ms Avg: 9.900us
UnBindBitmaps: Sum: 1.123ms Avg: 4.679us
Done Dumping Averages
concurrent copying paused: Sum: 557.123ms 99% C.I. 0.041ms-119.999ms Avg: 2.321ms Max: 175.712ms
concurrent copying freed-bytes: Avg: 28MB Max: 131MB Min: 895KB
Freed-bytes histogram: 0:10,10240:18,20480:139,30720:47,40960:11,51200:4,61440:8,81920:1,112640:1,133120:1
concurrent copying total time: 107.857s mean time: 449.408ms
concurrent copying freed: 103172771 objects with total size 6954MB
concurrent copying throughput: 956570/s / 64MB/s per cpu-time: 123908015/s / 118MB/s
concurrent copying tracing throughput: 64MB/s per cpu-time: 118MB/s
Average major GC reclaim bytes ratio 0.283573 over 240 GC cycles
Average major GC copied live bytes ratio 0.169941 over 244 major GCs
Cumulative bytes moved 1429791648
Cumulative objects moved 24194413
Peak regions allocated 654 (163MB) / 2048 (512MB)
Total madvise time 6.044s
Start Dumping Averages for......
......
Done Dumping Averages
......
trace-堆栈头部参数
"ActivityManager" prio=5 tid=24 Blocked
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x15005108 self=0xb400007aa0a1c030
| sysTid=2496 nice=-2 cgrp=foreground sched=0/0 handle=0x78977a2cb0
| state=S schedstat=( 77089777437 56649675347 158258 ) utm=4952 stm=2756 core=0 HZ=100
| stack=0x789769f000-0x78976a1000 stackSize=1039KB
| held mutexes=
(1) 第1行
- “ActivityManager”:线程名,main表示主线程
- prio:线程优先级,通过java.lang.Thread.setPriority()设置,取值范围 [1, 10]。数值越大优先级越高。
- tid:进程内部的线程号,进程内唯一的编号。
- Blocked:线程状态,当前表示阻塞。其它具体取值见下。
(2) 第2行
- group: 线程所属的线程组。Java中使用ThreadGroup来表示线程组,便于对一批线程进行分类管理。默认情况下,所有的线程都属于主线程组。
- sCount: 线程被挂起的次数
- dsCount: 线程因调试而被挂起的次数
- obj: 当前线程关联的java线程对象
- self: 当前线程地址
(3) 第3行
- sysTid:系统线程号,系统内唯一的编号,日志中见到的线程号就是sysTid;
- nice: 调度的优先级,通过android.os.Process.setThreadPriority()设置,取值范围 [-20, 19],数值越低优先级越高,与prio相反;
- cgrp: 进程所属的进程调度组,可以用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等);
- sched: 调度策略
- handle: 函数处理地址
(4) 第4行
state: 线程的调度状态,见下;
- schedstat: CPU调度时间统计, 依次为Running(CPU运行的时间,单位ns)、Runable(RQ队列的等待时间,单位ns)、Switch(CPU调度切换次数)。可通过adb shell cat /proc/[pid]/task/[tid]/schedstat查看;
- utm:CPU在用户态(u表示user)运行的时间,单位为jiffies,默认10ms
- stm: CPU在内核态(s表示system)的CPU时间,单位为jiffies,默认10ms,schedstat第一个值=utm + stm
- core: 该线程的最后运行所在核
- HZ: 时钟频率
(5) 第5行
- stack:线程栈的地址区间
- stackSize:栈的大小,Android中主线程栈大小默认为8M,子线程栈约1M。
(6) 第6行
- mutex: 所持有mutex类型,有独占锁exclusive和共享锁shared两类
trace-堆栈信息
堆栈信息示例:
"main" prio=5 tid=1 Native
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x715d2848 self=0xb400007aa09d8380
| sysTid=2297 nice=-2 cgrp=foreground sched=0/0 handle=0x7c616924f8
| state=S schedstat=( 106013375008 95074030053 247686 ) utm=7925 stm=2675 core=5 HZ=100
| stack=0x7fc5d8e000-0x7fc5d90000 stackSize=8188KB
| held mutexes=
native: #00 pc 00000000000a2bcc /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+12)
native: #01 pc 0000000000017ea8 /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
native: #02 pc 0000000000017d84 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+116)
native: #03 pc 0000000000154520 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+48)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:337)
at android.os.Looper.loopOnce(Looper.java:168)
at android.os.Looper.loop(Looper.java:299)
at com.android.server.SystemServer.run(SystemServer.java:926)
at com.android.server.SystemServer.main(SystemServer.java:621)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1023)
从“held mutexes=”之后便表示该线程在某一时间片上的堆栈信息,执行顺序是从下到上。
“at”后表示执行的java代码,以at android.os.Looper.loop(Looper.java:299)为例,表示执行的是android.os包下的Looper.java文件下的loop()函数,位于299行。
“native”后表示执行的是C/C++代码,如:
native: #01 pc 0000000000017ea8 system/lib64/libutils.so (android::Looper::pollInner(int)+184)
- #01应该表示native堆栈编号,以#00开始从上向下递增;
- pc 0000000000017ea8:不知
- /system/lib64/libutils.so表示该C++代码位于的动态库路径;
- android::Looper::pollInner(int)表示namespace为android::Looper,执行的函数为pollInner(int);
- +184:暂时不知;
trace-Waiting Channels
表示线程在内核中的执行情况,及正在执行内核中的哪个函数。线程的状态与内核中的执行函数有时候有一定的关联,但并不是一一对应的关系,具体情况要根据代码分析。
----- Waiting Channels: pid 进程号 at 日期 时间 -----
Cmd line: 进程名
sysTid=线程号 线程在内核中的执行情况
sysTid=4147 binder_wait_for_work
sysTid=4157 0
sysTid=4158 pipe_read
sysTid=4159 futex_wait_queue_me
......
sysTid=4162 futex_wait_queue_me
sysTid=4164 binder_wait_for_work
sysTid=4227 do_epoll_wait
当发生ANR等问题时,常见的线程在内核中的执行函数有:do_epoll_wait、do_sigtimedwait、futex_wait_queue_me、unix_stream_data_wait等。
(1) do_epoll_wait
表示线程正在等候相关epoll事务的产生,相当于正在监听。一但等候到该时间,则线程会立刻运行。通常对应的线程状态有:
- Blocked:可能线程正在等锁。在堆栈中的java代码正执行到waiting to lock处。如
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x715d2848 self=0xb400007aa09d8380
| sysTid=2297 nice=-2 cgrp=foreground sched=0/0 handle=0x7c616924f8
| state=S schedstat=( 105835425539 94348256666 247165 ) utm=7913 stm=2670 core=5 HZ=100
| stack=0x7fc5d8e000-0x7fc5d90000 stackSize=8188KB
| held mutexes=
at com.android.server.alarm.AlarmManagerService$DeliveryTracker.alarmTimedOut(AlarmManagerService.java:5009)
- waiting to lock <0x027c9109> (a java.lang.Object) held by thread 215
at com.android.server.alarm.AlarmManagerService$AlarmHandler.handleMessage(AlarmManagerService.java:4402)
......
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1023)
此时的内核正在执行:sysTid=2297 do_epoll_wait
- Native:该线程可能正在等待消息,在堆栈中的java代码正执行到MessageQueue.nativePollOnce()。如:
"android.io" prio=5 tid=14 Native
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x15002108 self=0xb400007aa0a07270
| sysTid=2486 nice=0 cgrp=foreground sched=0/0 handle=0x78a2304cb0
| state=S schedstat=( 10541856295 3817106609 6070 ) utm=966 stm=87 core=4 HZ=100
| stack=0x78a2201000-0x78a2203000 stackSize=1039KB
| held mutexes=
......
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:337)
at android.os.Looper.loopOnce(Looper.java:168)
at android.os.Looper.loop(Looper.java:299)
at android.os.HandlerThread.run(HandlerThread.java:67)
at com.android.server.ServiceThread.run(ServiceThread.java:46)
此时的内核正在执行:sysTid=2486 do_epoll_wait
(2) futex_wait_queue_me
这个内核函数使当前线程主动释放CPU进入等待状态,若没有被唤醒,就一直停在这个函数中。通常对应的线程状态有:
- Native:线程执行C/C++代码时等待锁,如以下 native: #01中的WaitHoldingLocks():
"Jit thread pool worker thread 0" daemon prio=5 tid=4 Native
| group="system" sCount=1 ucsCount=0 flags=1 obj=0x15000af0 self=0xb400007aa09e6200
| sysTid=2386 nice=9 cgrp=foreground sched=0/0 handle=0x7916c2bcb0
| state=S schedstat=( 24831233130 34251463605 40586 ) utm=1839 stm=643 core=6 HZ=100
| stack=0x7916b2c000-0x7916b2e000 stackSize=1023KB
| held mutexes=
native: #00 pc 000000000004ddd0 /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32)
native: #01 pc 000000000028dc74 /apex/com.android.art/lib64/libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+152)
native: #02 pc 00000000006a5a48 /apex/com.android.art/lib64/libart.so (art::ThreadPool::GetTask(art::Thread*)+128)
native: #03 pc 00000000006a4df8 /apex/com.android.art/lib64/libart.so (art::ThreadPoolWorker::Run()+148)
此时的内核正在执行:sysTid=2386 futex_wait_queue_me
- Waiting:线程执行了wait方法之后,进入到”Wait Set”队列,等待其它线程执行notify方法,并唤醒自己。
"FinalizerDaemon" daemon prio=5 tid=6 Waiting
| group="system" sCount=1 ucsCount=0 flags=1 obj=0x15000cf8 self=0xb400007aa09e0e90
| sysTid=2389 nice=4 cgrp=foreground sched=0/0 handle=0x7916911cb0
| state=S schedstat=( 1544421438 987348692 2727 ) utm=92 stm=62 core=5 HZ=100
| stack=0x791680e000-0x7916810000 stackSize=1039KB
| held mutexes=
at java.lang.Object.wait(Native method)
- waiting on <0x0eb9619f> (a java.lang.Object)
at java.lang.Object.wait(Object.java:442)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:190)
- locked <0x0eb9619f> (a java.lang.Object)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:211)
at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:273)
at java.lang.Daemons$Daemon.run(Daemons.java:139)
at java.lang.Thread.run(Thread.java:920)
其它
trace中的堆栈不一定是ANR现场
trace抓取的是时间片,表示文件所标识的时刻下抓取的时间片正在执行的代码。
当发生ANR时,有可能因为系统负载大导致抓取trace的线程并没有抓取到ANR现场,而是有一定的延迟。除非线程一致是Blocked,则它的堆栈状态就可以一直延续到抓取trace时,这时可以分析出一些信息。
当trace中线程的状态为blocked,waiting,且时间距离ANR不远时可以认为当前堆栈是ANR现场。如果为其它状态,要视具体情况而定:
- 若此时堆栈里执行的函数有耗时操作,则该堆栈有较大可能为ANR现场;
- 若堆栈里的函数无耗时操作,有可能ANR打印堆栈的时候,主线程已经恢复正常。此时的堆栈不是ANR现场。
1个jiffies是多少秒?
Linux中有几个与时间相关的单位或变量。
- HZ:Linux内核通过编程预设系统定时器的时钟频率,它表示1秒钟内时钟中断的次数,即1秒钟内,系统时钟的节拍次数。在2.5版本之前频率为100,2.5版内核开始把频率从100调高到1000。
- Tick:Tick是HZ的倒数,即发生一次时钟中断间隔的时间。当HZ=100时,tick为10毫秒。
- jiffies:它是一个全局变量,用来记录系统自启动以来产生了多少tick。
所以,系统运行时间(以秒为单位):system_time=(jiffies)/HZ。通常部分变量以jiffies计时,如耗时多少jiffies,可以理解为1jiffies=1tick=1/HZ(单位为s);
schedstat与utm/stm的关系
schedstat参数中的第一个数字与utm和stm相关,schedstat括号中的3个数字依次是Running、Runable、Switch。
- Running时间:CPU运行的时间,单位ns
- Runable时间:线程的等待时间,单位ns
- Switch次数:CPU调度切换次数
然后是utm和stm值。
- utm: 该线程在用户态所执行的时间,单位是jiffies,默认等于10ms
- stm: 该线程在内核态所执行的时间,单位是jiffies,默认等于10ms
以下面的线程状态为例:
schedstat=( 77089777437 56649675347 158258 ) utm=4952 stm=2756 core=0 HZ=100
可见,该线程Running=77089777437ns,约等于77089ms。在CPU运行时间包括用户态(utm)和内核态(stm)。 utm + stm = (4952 + 2756) ×10 ms = 77080ms。
所以,utm + stm = schedstat第一个参数值。
Android的线程状态
分析ANR问题时,通常会分析trace文件,它记录了手机发生ANR时,各个进程里的所有线程在当时的状态。如:
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x7325d368 self=0xb400007f3548bc00
| sysTid=12461 nice=0 cgrp=default sched=0/0 handle=0x7fbc9744f8
| state=S schedstat=( 503832573 359204476 930 ) utm=40 stm=9 core=1 HZ=100
| stack=0x7fe21d0000-0x7fe21d2000 stackSize=8192KB
| held mutexes=
native: #00 pc 00000000000d7a58 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
native: #01 pc 0000000000019acc /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
native: #02 pc 00000000000199ac /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
native: #03 pc 0000000000119848 /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:335)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:8145)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
第一行中的Native就是线程状态,除此之外还有Monitor等其它状态。
在java中,有6种线程状态,定义在/java/lang/Thread.java中,分别如下:
- NEW:The thread has been created, but has never been started.
- RUNNABLE:The thread may be run.
- BLOCKED:The thread is blocked and waiting for a lock.
- WAITING:The thread is waiting.
- TIMED_WAITING:The thread is waiting for a specified amount of time.
- TERMINATED:The thread has been terminated.
Android中不仅有Java代码,也有C++代码。C++定义的线程状态与Java略有不同,C++ thread有10种状态, 对应着Java thread的6种状态。其对应关系如下(右侧为Android trace文件中的线程状态):
| Java thread | C++ thread | 说明 |
|---|---|---|
| New | Initializing | 新建,正在初始化,为其分配资源 |
| New | Starting | 新建,正在启动 |
| Waiting | Wait | 执行了无超时参数的wait函数 |
| Waiting | VMWait | 正在等待VM资源 |
| TimedWaiting | TimedWait | 执行了带有超时参数的wait、sleep或join函数 |
| Runnable | Running Runnable | 线程可运行(等待CPU资源)或正在运行 |
| Runnable | Native | 正在执行Native层代码 |
| Runnable | Suspended | 线程暂停,通常是由于GC或Debug |
| Blocked | Monitor | 线程阻塞,等待获取对象锁 |
| Terminated | Zombie | 线程死亡,停止运行 |
| Sleeping | 执行了java.lang.Thread.sleep() | |
| Unknown | 未知状态 |
如果线程正在执行C/C++代码(堆栈中最上层为native: #00),则线程状态展示的是C++ thread中的10种状态之一;若执行的是java代码,则展示的线程状态是Java thread对应的的6种状态之一。
trace中两个线程优先级的区别
trace中每个线程有两个优先级,分别为prio和nice。
"线程名" prio=5 tid=24 Blocked
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x15005108 self=0xb400007aa0a1c030
| sysTid=2496 nice=-2 cgrp=foreground sched=0/0 handle=0x78977a2cb0
- prio优先级
用java.lang.Thread.setPriority(int newPriority)设置,取值范围 [1, 10]。数值越大优先级越高。
示例代码:
Runnable r = ...;
Thread thread = new Thread(r);
thread.setPriority((Thread.MAX_PRIORITY + Thread.NORM_PRIORITY) / 2);
- nice优先级
通过android.os.Process.setThreadPriority(int tid, int priority)设置,取值范围 [-20, 19],数值越低优先级越高,与prio相反;
示例代码:
Runnable r = ...
Thread thread = new Thread( new Runnable() {
public void run() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE);
r.run();
}
});
Thread.setPriority()与Process.setThreadPriority()在底层实现上实际是相同的,以Android 4.0.3为例,在源码/dalvik/vm/os/android.cpp下定义了java线程优先级到Android的映射方式。在Android源码中,通常使用Android线程优先级常量为标准,使系统的其他部分与其保持一致。
/dalvik/vm/os/android.cpp中初始优先级数组kNiceValues[10] = {19,16,13,10,0,-2,-4,-5,-6,-8}。
/*
* Conversion map for "nice" values.
*
* We use Android thread priority constants to be consistent with the rest
* of the system. In some cases adjacent entries may overlap.
*/
static const int kNiceValues[10] = {
ANDROID_PRIORITY_LOWEST, /* 1 (MIN_PRIORITY) */
ANDROID_PRIORITY_BACKGROUND + 6,
ANDROID_PRIORITY_BACKGROUND + 3,
ANDROID_PRIORITY_BACKGROUND,
ANDROID_PRIORITY_NORMAL, /* 5 (NORM_PRIORITY) */
ANDROID_PRIORITY_NORMAL - 2,
ANDROID_PRIORITY_NORMAL - 4,
ANDROID_PRIORITY_URGENT_DISPLAY + 3,
ANDROID_PRIORITY_URGENT_DISPLAY + 2,
ANDROID_PRIORITY_URGENT_DISPLAY /* 10 (MAX_PRIORITY) */
};
代码中的ANDROID_PRIORITY_xxxx系列参数定义在/frameworks/base/include/utils/threads.h中。
enum {
/*
* ***********************************************
* ** Keep in sync with android.os.Process.java **
* ***********************************************
*
* This maps directly to the "nice" priorities we use in Android.
* A thread priority should be chosen inverse-proportionally to
* the amount of work the thread is expected to do. The more work
* a thread will do, the less favorable priority it should get so that
* it doesn't starve the system. Threads not behaving properly might
* be "punished" by the kernel.
* Use the levels below when appropriate. Intermediate values are
* acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below.
*/
ANDROID_PRIORITY_LOWEST = 19,
/* use for background tasks */
ANDROID_PRIORITY_BACKGROUND = 10,
/* most threads run at normal priority */
ANDROID_PRIORITY_NORMAL = 0,
/* threads currently running a UI that the user is interacting with */
ANDROID_PRIORITY_FOREGROUND = -2,
/* the main UI thread has a slightly more favorable priority */
ANDROID_PRIORITY_DISPLAY = -4,
/* ui service treads might want to run at a urgent display (uncommon) */
ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY,
/* all normal audio threads */
ANDROID_PRIORITY_AUDIO = -16,
/* service audio threads (uncommon) */
ANDROID_PRIORITY_URGENT_AUDIO = -19,
/* should never be used in practice. regular process might not
* be allowed to use this level */
ANDROID_PRIORITY_HIGHEST = -20,
ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL,
ANDROID_PRIORITY_MORE_FAVORABLE = -1,
ANDROID_PRIORITY_LESS_FAVORABLE = +1,
};
java Thread的优先级newPriority取值为[1, 10],具体的映射实现如下:
void os_changeThreadPriority(Thread* thread, int newPriority)
{
if (newPriority < 1 || newPriority > 10) {
LOGW("bad priority %d", newPriority);
newPriority = 5;
}
int newNice = kNiceValues[newPriority-1];
......
}
通过映射后,java Thread的newNice = kNiceValues[newPriority-1]。如prio=10映射为nice=-8;prio=5映射为nice=0;prio=1映射为nice=19。
另外就是prio和nice的值是相互独立的,如使用Android提供的Process.setThreadPriority()修改优先级后,再使用java中的Thread.getPriority()获取优先级,则Java Thread下的优先级数值依旧没有改变(参考:剖析Android中进程与线程调度之nice,未做过验证)。个人理解,线程在系统中优先级应该以最后一次的set方法为准。
在Android中优先使用android.os.Process.setThreadPriority(int tid, int priority)设置优先级。
Google在Android 5.0之后用ART替代了Dalvik,以上相关源码在ART中的实现暂未找到。