Systrace 基础知识 - 锁竞争解读

参考连接:https://www.androidperformance.com/2019/12/06/Android-Systrace-Binder/

1.Systrace 显示的锁的信息

Systrace 基础知识 - 锁竞争解读_第1张图片

monitor contention with owner Binder:1605_B (4667) at void
com.android.server.wm.ActivityTaskManagerService.activityPaused(android.os.IBinder)
(ActivityTaskManagerService.java:1733) waiters=2 blocking from android.app.ActivityManager$StackInfo 
com.android.server.wm.ActivityTaskManagerService.getFocusedStackInfo()(ActivityTaskManagerService.java:2064)

上面的话分两段来看,以 blocking 为分界线

1.1 第一段信息解读

monitor contention with owner Binder:1605_B (4667) at void 
com.android.server.wm.ActivityTaskManagerService.activityPaused(android.os.IBinder)
(ActivityTaskManagerService.java:1733) waiters=2

Monitor 指的是当前锁对象的池,在 Java 中,每个对象都有两个池,锁(monitor)池和等待池:

锁池(同步队列 SynchronizedQueue ):假设线程 A 已经拥有了某个对象(注意:不是类 )的锁,而其它的线程想要调用这个对象的某个 synchronized 方法(或者 synchronized 块),由于这些线程在进入对象的 synchronized 方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程 A 拥有,所以这些线程就进入了该对象的锁池中。

这里用了争夺(contention)这个词,意思是这里由于在和目前对象的锁正被其他对象(Owner)所持有,所以没法得到该对象的锁的拥有权,所以进入该对象的锁池

Owner : 指的是当前拥有这个对象的锁的对象。这里是 Binder:1605_B,4667 是其线程 ID。

at 后面跟的是拥有这个对象的锁的对象正在做什么。这里是在执行 void com.android.server.wm.ActivityTaskManagerService.activityPaused 这个方法,其代码位置是 :ActivityTaskManagerService.java:1733 其对应的代码如下:

com/android/server/wm/ActivityTaskManagerService.java
@Override
public final void activityPaused(IBinder token) {
    final long origId = Binder.clearCallingIdentity();
    synchronized (mGlobalLock) { // 1733 是这一行
        ActivityStack stack = ActivityRecord.getStackLocked(token);
        if (stack != null) {
            stack.activityPausedLocked(token, false);
        }
    }
    Binder.restoreCallingIdentity(origId);
}

可以看到这里 synchronized (mGlobalLock) ,获取了 mGlobalLock 锁的拥有权,在他释放这个对象的锁之前,任何其他的调用 synchronized (mGlobalLock) 的地方都得在锁池中等待

waiters 值得是锁池里面正在等待锁的操作的个数;这里 waiters=2 表示目前锁池里面已经有一个操作在等待这个对象的锁释放了,加上这个的话就是 3 个了

1.2 第二段信息解读

blocking from android.app.ActivityManager$StackInfo com.android.server.wm.ActivityTaskManagerService.getFocusedStackInfo()(ActivityTaskManagerService.java:2064) 

第二段信息相对来说简单一些,就是标识了当前被阻塞等锁的方法 , 这里是 ActivityManager 的 getFocusedStackInfo 被阻塞,其对应的代码

com/android/server/wm/ActivityTaskManagerService.java
@Override
public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
    enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
    long ident = Binder.clearCallingIdentity();
    try {
        synchronized (mGlobalLock) { // 2064 是这一行 
            ActivityStack focusedStack = getTopDisplayFocusedStack();
            if (focusedStack != null) {
                return mRootActivityContainer.getStackInfo(focusedStack.mStackId);
            }
            return null;
        }
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

可以看到这里也是调用了 synchronized (ActivityManagerService.this) ,从而需要等待获取 ams 对象的锁拥有权

1.3 总结

上面这段话翻译过来就是ActivityTaskManagerService 的 getFocusedStackInfo 方法在执行过程中被阻塞,原因是因为执行同步方法块的时候,没有拿到同步对象的锁的拥有权;需要等待拥有同步对象的锁拥有权的另外一个方法ActivityTaskManagerService.activityPaused 执行完成后,才能拿到同步对象的锁的拥有权,然后继续执行

可以对照原文看上面的翻译

monitor contention with owner Binder:1605_B (4667)
at void com.android.server.wm.ActivityTaskManagerService.activityPaused(android.os.IBinder)(ActivityTaskManagerService.java:1733)
waiters=2
blocking from android.app.ActivityManager$StackInfo com.android.server.wm.ActivityTaskManagerService.getFocusedStackInfo()(ActivityTaskManagerService.java:2064)

2.等锁分析

还是上面那个 Systrace,Binder 信息里面显示 waiters=2,意味着前面还有两个操作在等锁释放,也就是说总共有三个操作都在等待 Binder:1605_B (4667) 释放锁,我们来看一下 Binder:1605_B 的执行情况
Systrace 基础知识 - 锁竞争解读_第2张图片

从上图可以看到,Binder:1605_B 正在执行 activityPaused,中间也有一些其他的 Binder 操作,最终 activityPaused 执行完成后,释放锁

下面我们就把这个逻辑里面的执行顺序理顺,包括两个 waiters

2.1 锁等待

Systrace 基础知识 - 锁竞争解读_第3张图片
上图中可以看到 mGlobalLock 这个对象锁的争夺情况

  1. Binder_1605_B 首先开始执行 activityPaused,这个方法中是要获取 mGlobalLock 对象锁的,由于此时mGlobalLock 没有竞争,所以 activityPaused 获取对象锁之后开始执行
  2. android.display 线程开始执行 checkVisibility 方法,这个方法也是要获取 mGlobalLock 对象锁的,但是此时Binder_1605_B 的 activityPaused 持有 mGlobalLock 对象锁 ,所以这里android.display 的 checkVisibility 开始等待,进入 sleep 状态
  3. android.anim线程开始执行 relayoutWindow 方法,这个方法也是要获取 mGlobalLock 对象锁的,但是此时Binder_1605_B 的 activityPaused 持有 mGlobalLock 对象锁 ,进入 sleep 状态
  4. android.bg 线程开始执行getFocusedStackInfo 方法,这个方法也是要获取 mGlobalLock 对象锁的,但是此时 Binder_1605_B
    的 activityPaused 持有 mGlobalLock 对象锁 ,进入 sleep 状态
    经过上面四步,就形成了 Binder_1605_B 线程在运行,其他三个争夺 mGlobalLock 对象锁失败的线程分别进入 sleep 状态,等待 Binder_1605_B 执行结束后释放 mGlobalLock 对象锁

2.2 锁释放

Systrace 基础知识 - 锁竞争解读_第4张图片

上图可以看到 mGlobalLock 锁的释放和后续的流程

  1. Binder_1605_B 线程的 activityPaused 执行结束,mGlobalLock 对象锁释放
  2. 第一个进入等待的android.display 线程开始执行 checkVisibility 方法 ,这里从 android.display线程的唤醒信息可以看到,是被 Binder_1605_B(4667) 唤醒的
  3. android.display 线程的checkVisibility 执行结束,mGlobalLock 对象锁释放
  4. 第二个进入等待的 android.anim 线程开始执行relayoutWindow 方法 ,这里从 android.anim 线程的唤醒信息可以看到,是被 android.display(1683) 唤醒的
  5. android.anim 线程的 relayoutWindow执行结束,mGlobalLock 对象锁释放
  6. 第三个进入等待的 android.bg 线程开始执行getFocusedStackInfo 方法 ,这里从 android.bg 线程的唤醒信息可以看到,是被
    android.anim(1684) 唤醒的

经过上面 6 步,这一轮由于 mGlobalLock 对象锁引起的等锁现象结束。这里只是一个简单的例子,在实际情况下,SystemServer 中的 BInder 等锁情况会非常严重,经常 waiter 会到达 7 - 10 个,非常恐怖,比如下面这种:

Systrace 基础知识 - 锁竞争解读_第5张图片

你可能感兴趣的:(调试工具)