0%

性能优化-:(07)2022.2.10-卡顿和布局优化---路哥

一、前情回顾

GC 的时候,Java 线程、Native 线程都会中断,中断就会卡顿,会影响性能。

上节课提到,执行一次 GC 后,间隔5s 或者 500ms 再次执行一下GC ,这样基本上能触发 GC ,这是什么原理呢?在 Android 5.0 以前,System.gc() 基本上就能触发 GC 行为,它的代码是这样的:

1
2
3
4
5
6
7
8
/**
* Indicates to the VM that it would be a good time to run the
* garbage collector. Note that this is a hint only. There is no guarantee
* that the garbage collector will actually be run.
*/
public static void gc() {
Runtime.getRuntime().gc();
}

但是在 5.0 及以后,调用 System.gc 或者 runTime.gc 不一定会触发 GC 了,这是因为在 5.0 及以后的 gc 方法里面会有标记判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Indicates to the VM that it would be a good time to run the
* garbage collector. Note that this is a hint only. There is no guarantee
* that the garbage collector will actually be run.
*/
public static void gc() {
boolean shouldRunGC;
synchronized(lock) {
shouldRunGC = justRanFinalization;
if (shouldRunGC) {
justRanFinalization = false;
} else {
runGC = true;
}
}
if (shouldRunGC) {
Runtime.getRuntime().gc();
}
}

可以看到,是否执行 gc 还有赖于 justRanFinalization 变量,这个变量在哪里赋值为true 呢?是在 runFinalization 方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Provides a hint to the VM that it would be useful to attempt
* to perform any outstanding object finalization.
*/
public static void runFinalization() {
boolean shouldRunGC;
synchronized(lock) {
shouldRunGC = runGC;
runGC = false;
}
if (shouldRunGC) {
Runtime.getRuntime().gc();
}
Runtime.getRuntime().runFinalization();
synchronized(lock) {
justRanFinalization = true;
}
}

从上述代码可以看出,我们直接调用 System.gc 并不会调用到 Runtime.getRunTime().gc() ,只是做了个标记将 runGc 设置为 true ,然后在下一次 GC 的时候,就能真正 GC 了。那为什么要间隔5s 或者 500ms 呢?这个跟线程调度、线程的中断状态有关。

所以,上一节课也提到,内存监控自己做 GC 的时候,也可以使用 runFinalization() + runTime.gc 的方式去GC,这样也是可以的。

由于路哥在课程里面对这个讲得不是太清晰,更详细的内容可以参考

二、 Alpha 源码讲解

面试中怎么讲:

  • Executor :线程池,调用方可以自己实现,也可以使用默认的,参数怎么设置

  • Task : 是个线程,有任务自己的状态、有自己的子任务

  • 接口: 执行前、执行后、失败

仿照 Alpha 的框架没有实现 DAG 算法,是个弊端

1、主要要实现的功能

线程池、线程等待、线程切换、主进程/子进程

三、总结之前的启动框架

内存、ANR 、启动,这3个点,性能优化就能把握了

四、卡顿

根据前面说的,能够在 Activity 的 onWindowFocusChanged 方法中停止方法的采集,因为这个时候恰好是 window 切换,要么是新的 window 启动了,要么是关闭了某个 window,所以,如果我们要监测到第一个 Activity 的耗时,可以从Application 的 onCreate 中去start,在 MainActivity 中去 stop 这个 trace 去实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
//Application
class MyApplication : Application {
override fun onCreate(xx) {
Debug.startMethodTracingSampling(tracePath, bufferSize, untervalUs)
}
}

//MainActivity
class MainActivity : Activity {
override fun onWindowFocusChanged(hasFocus) {
Debug.stopMethodTracing()
}
}

你可以获取到更细粒度更精准的耗时,比如在 ListView 中 getView 方法,在方法调用前start ,在调用完成之后,调用 stop 。

根据上述获取的 trace 文件,拖到 AS 中就能看到方法的耗时,线程相关内容等。

systrace 比上面的方法更加精准。systrace 就是个 shell 脚本嘛,他需要使用 python 执行,比如定位到 SDK 目录下(platform-tools)的 systrace 时,调用 python systrace.py 即可。systrace 可以在 chrome 上打开,使用地址:

chrome://tracing

线上如何做卡顿。卡顿的原理。自己定义阈值,超过某个值就说是卡顿,比如 1000ms ,

CountDownLatch 实现让主线程等待所有的初始化完成才继续执行的。

第一个版本仿照 Alpha ,怎么保证顺序?

第二个版本 DAG (有向无环图)保证顺序

谢谢你的鼓励