Systrace - 锁竞争解读

1.Systrace 显示的锁的信息


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 执行完成后,才能拿到同步对象的锁的拥有权,然后继续执行


2.等锁分析


还是上面那个 Systrace,Binder 信息里面显示 waiters=2,意味着前面还有两个操作在等锁释放,也就是说总共有三个操作都在等待 Binder:1605_B (4667) 释放锁,我们来看一下 Binder:1605_B 的执行情况


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

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

2.1 锁等待

上图中可以看到 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 锁释放


上图可以看到 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 个,非常恐怖,比如下面这种:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值