这是性能优化系列之matrix框架的第15
篇文章,我将在性能优化专栏中对matrix apm框架做一个全面的代码分析,性能优化是Android高级工程师必知必会的点,也是面试过程中的高频题目,对性能优化感兴趣的小伙伴可以去我主页查看所有关于matrix的分享。
前言
这是matrix卡顿优化分析的最后一篇,在这之后我们将会开始matrix中其他类型性能优化的分析。我们用了10多篇的篇幅完整的介绍了matrix中卡顿优化相关的内容,总结下来卡顿监控的方向有三种:
- 借助主线程消息队列提供的printer机制实现监控-Android性能优化系列-腾讯matrix-TracePlugin卡顿优化之LooperMonitor源码分析
- hook IdleHandler实现监控-Android性能优化系列-腾讯matrix卡顿优化之IdleHandlerLagTracer源码分析
- 是借助系统的输入系统机制实现触摸事件的卡顿监控-Android性能优化系列-腾讯matrix卡顿优化之TouchEventLagTracer源码分析
今天要进行的分析是线程优先级导致的卡顿优化。为什么说线程优先级会导致卡顿问题?因为不同线程要根据业务的优先级设置合适的优先级,试想假如主线程设置了极低的优先级,导致迟迟得不到cpu的调度,是不是会导致主线程执行缓慢呢?这就是为什么matrix中的卡顿监控也包含线程优先级这一块的原因。
接下来步入正题,继续从几个关键方法入手进行代码分析。
- 构造方法
- onStartTrace
- onStopTrace
构造方法
没有操作
onStartTrace
onStartTrace会调用到onAlive,onAlive进入了native层做初始化。
@Override
protected void onAlive() {
super.onAlive();
nativeInitMainThreadPriorityDetective();
}
我们找到MatrixTracer.cc中找到nativeInitMainThreadPriorityDetective方法,可以看到,这里hook了两个方法,一个是设置线程优先级的setpriority方法,确保住主线程优先级不被设置的过低,如果对主线程设置过低的优先级(过高的nice值),就会被监控到,从而上报问题提示开发者;另一个hook的方法是TimerSlack的prctl方法,确保主线程的TimerSlack值不被设置的过大。
static void nativeInitMainThreadPriorityDetective(JNIEnv *env, jclass) {
xhook_grouped_register(HOOK_REQUEST_GROUPID_THREAD_PRIO_TRACE, ".*\.so$", "setpriority",
(void *) my_setpriority, (void **) (&original_setpriority));
xhook_grouped_register(HOOK_REQUEST_GROUPID_THREAD_PRIO_TRACE, ".*\.so$", "prctl",
(void *) my_prctl, (void **) (&original_prctl));
xhook_refresh(true);
}
TimerSlack是Linux系统为了降低系统功耗,避免timer时间参差不齐,过于的频繁的唤醒cpu,而设置的一种对齐策略。如果系统设置了大于等于10的nice值,即设置了比后台优先级还要低的优先级,即把线程设置成了后台线程,那么系统就会设置一个比较高的TimerSlack,从默认的50微秒,提高到40毫秒,从而导致wait/sleep等挂起的时间多了40ms左右。详见Android的离奇陷阱 — 设置线程优先级导致的微信卡顿惨案。
hook点设置好后,看看拦截后做了什么。
my_setpriority
在设置优先级之前,先拿到之前的优先级,和当前即将更新的优先级一块传递到Java层,也就是调用到了ThreadPriorityTracer中的onMainThreadPriorityModified方法,将优先级变化的信息提示出来,这样开发者就能知道哪里更新了主线程优先级,从而规避问题。
int my_setpriority(int __which, id_t __who, int __priority) {
if ((__who == 0 && getpid() == gettid()) || __who == getpid()) {
int priorityBefore = getpriority(__which, __who);
JNIEnv *env = JniInvocation::getEnv();
env->CallStaticVoidMethod(gJ.ThreadPriorityDetective, gJ.ThreadPriorityDetective_onMainThreadPriorityModified, priorityBefore, __priority);
}
return original_setpriority(__which, __who, __priority);
}
my_prctl
当TimerSlack值被改变时,判断如果大于50000微妙,也就是50毫秒,超过这个值就调用到Java层上报信息,帮助开发人员规避问题。
int my_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) {
if(option == PR_SET_TIMERSLACK) {
if (gettid()==getpid() && arg2 > 50000) {
JNIEnv *env = JniInvocation::getEnv();
env->CallStaticVoidMethod(gJ.ThreadPriorityDetective, gJ.ThreadPriorityDetective_onMainThreadTimerSlackModified, arg2);
}
}
return original_prctl(option, arg2, arg3, arg4, arg5);
}
onStopTrace
没有特别的操作,不用关注
总结
线程优先级的监控实现比较简单,通过hook底层的setPriority(设置线程优先级的方法)和prctl(设置TimerSlack值得方法)方法,实现对线程优先级和线程TimerSlack值发生变化得监听,从而规避由线程优先级设置不当导致得问题。
至此我们对matrix中卡顿优化相关得内容就全部分析完了,建议读者可以再回顾一下前边这十几篇文章的内容,提炼总结一下卡顿监控方面的知识点,形成自己的知识网络,从而可以更好的应对面试中性能优化相关的题目。卡顿监控是性能优化中的重中之重,因为卡顿对用户来说是最直观的问题,最有可能导致用户流失,所以要重点留意。