扣丁書屋

Android Systrace 基礎知識(10) - Binder 和鎖競爭解讀

本文是 Systrace 系列文章的第十篇,主要是對 Systrace 中的 Binder 和鎖信息進行簡單介紹,簡單介紹了 Binder 的情況,介紹了 Systrace 中 Binder 通信的表現形式,以及 Binder 信息查看,SystemServer 鎖競爭分析等

本系列的目的是通過 Systrace 這個工具,從另外一個角度來看待 Android 系統整體的運行,同時也從另外一個角度來對 Framework 進行學習。也許你看了很多講 Framework 的文章,但是總是記不住代碼,或者不清楚其運行的流程,也許從 Systrace 這個圖形化的角度,你可以理解的更深入一些。

系列文章目錄

  1. [Systrace 簡介[1]]
  2. [Systrace 基礎知識 - Systrace 預備知識[2]]
  3. [Systrace 基礎知識 - Why 60 fps ?[3]]
  4. [Systrace 基礎知識 - SystemServer 解讀[4]]
  5. [Systrace 基礎知識 - SurfaceFlinger 解讀[5]]
  6. [Systrace 基礎知識 - Input 解讀[6]]
  7. [Systrace 基礎知識 - Vsync 解讀[7]]
  8. [Systrace 基礎知識 - Vsync-App :基于 Choreographer 的渲染機制詳解[8]]
  9. [Systrace 基礎知識 - MainThread 和 RenderThread 解讀[9]]
  10. Systrace 基礎知識 - Binder 和鎖競爭解讀[10]
  11. Systrace 基礎知識 - Triple Buffer 解讀[11]
  12. Systrace 基礎知識 - CPU Info 解讀[12]

Binder 概述

Android 的大部分進程間通信都使用 Binder,這里對 Binder 不做過多的解釋,想對 Binder 的實現有一個比較深入的了解的話,推薦你閱讀下面三篇文章

  1. 理解 Android Binder 機制 1/3:驅動篇[13]
  2. 理解 Android Binder 機制 2/3:C++層[14]
  3. 理解 Android Binder 機制 3/3:Java 層[15]

「之所以要單獨講 Systrace 中的 Binder 和鎖,是因為很多卡頓問題和響應速度的問題,是因為跨進程 binder 通信的時候,鎖競爭導致 binder 通信事件變長,影響了調用端。最常見的就是應用渲染線程 dequeueBuffer 的時候 SurfaceFlinger 主線程阻塞導致 dequeueBuffer 耗時,從而導致應用渲染出現卡頓; 或者 SystemServer 中的 AMS 或者 WMS 持鎖方法等待太多, 導致應用調用的時候等待時間比較長導致主線程卡頓」

這里放一張文章里面的 Binder 架構圖 , 本文主要是以 Systrace 為主,所以會講 Systrace 中的 Binder 表現,不涉及 Binder 的實現

Binder 調用圖例

Binder 主要是用來跨進程進行通信,可以看下面這張圖,簡單顯示了在 Systrace 中 ,Binder 通信是如何顯示的

Binder 調用

圖中主要是 SystemServer 進程和 高通的 perf 進程通信,Systrace 中右上角 ViewOption 里面勾選 Flow Events 就可以看到 Binder 的信息

Binder

點擊 Binder 可以查看其詳細信息,其中有的信息在分析問題的時候可以用到

Binder 詳細信息

對于 Binder,這里主要介紹如何在 Systrace 中查看 Binder 「鎖信息」「鎖等待」這兩個部分,很多卡頓和響應問題的分析,都離不開這兩部分信息的解讀,不過最后還是要回歸代碼,找到問題后,要讀源碼來理順其代碼邏輯,以方便做相應的優化工作

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」 為分界線

第一段信息解讀

「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 個了

第二段信息解讀

「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 對象的鎖擁有權

總結

上面這段話翻譯過來就是

「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)」

等鎖分析

還是上面那個 Systrace,Binder 信息里面顯示 waiters=2,意味著前面還有兩個操作在等鎖釋放,也就是說總共有三個操作都在等待 Binder:1605_B (4667) 釋放鎖,我們來看一下 Binder:1605_B 的執行情況

等鎖分析

從上圖可以看到,Binder:1605_B 正在執行 activityPaused,中間也有一些其他的 Binder 操作,最終 activityPaused 執行完成后,釋放鎖

下面我們就把這個邏輯里面的執行順序理順,包括兩個 「waiters」

鎖等待

file:///Users/gaojack/blog/source/images/15756309922668.jpg

上圖中可以看到 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 對象鎖 ,所以這里 android.display 的 checkVisibility 開始等待,進入 sleep 狀態
  4. android.bg 線程開始執行 「getFocusedStackInfo」 方法,這個方法也是要獲取 mGlobalLock 對象鎖的,但是此時 Binder_1605_B 的 activityPaused 持有 mGlobalLock 對象鎖 ,所以這里 android.display 的 checkVisibility 開始等待,進入 sleep 狀態

經過上面四步,就形成了 Binder_1605_B 線程在運行,其他三個爭奪 mGlobalLock 對象鎖失敗的線程分別進入 sleep 狀態,等待 Binder_1605_B 執行結束后釋放 mGlobalLock 對象鎖

鎖釋放

鎖釋放

上圖可以看到 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 個,非??植?,比如下面這種:

大量的鎖等待

這也就可以解釋為什么 Android 手機 App 安裝多了、用的久了之后,系統就會卡的一個原因;另外重啟后也會有短暫的時候出現這種情況

如果不知道怎么查看喚醒信息,可以查看:Systrace 中查看進程信息喚醒[16] 這篇文章

相關代碼

Monitor 信息

art/runtime/monitor.cc

std::string Monitor::PrettyContentionInfo(const std::string& owner_name,
                                          pid_t owner_tid,
                                          ArtMethod* owners_method,
                                          uint32_t owners_dex_pc,
                                          size_t num_waiters) {
  Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
  const char* owners_filename;
  int32_t owners_line_number = 0;
  if (owners_method != nullptr) {
    TranslateLocation(owners_method, owners_dex_pc, &owners_filename, &owners_line_number);
  }
  std::ostringstream oss;
  oss << "monitor contention with owner " << owner_name << " (" << owner_tid << ")";
  if (owners_method != nullptr) {
    oss << " at " << owners_method->PrettyMethod();
    oss << "(" << owners_filename << ":" << owners_line_number << ")";
  }
  oss << " waiters=" << num_waiters;
  return oss.str();
}

Block 信息

art/runtime/monitor.cc

if (ATRACE_ENABLED()) {
  if (owner_ != nullptr) {  // Did the owner_ give the lock up?
    std::ostringstream oss;
    std::string name;
    owner_->GetThreadName(name);
    oss << PrettyContentionInfo(name,
                                owner_->GetTid(),
                                owners_method,
                                owners_dex_pc,
                                num_waiters);
    // Add info for contending thread.
    uint32_t pc;
    ArtMethod* m = self->GetCurrentMethod(&pc);
    const char* filename;
    int32_t line_number;
    TranslateLocation(m, pc, &filename, &line_number);
    oss << " blocking from "
        << ArtMethod::PrettyMethod(m) << "(" << (filename != nullptr ? filename : "null")
        << ":" << line_number << ")";
    ATRACE_BEGIN(oss.str().c_str());
    started_trace = true;
  }
}

參考

  1. 理解 Android Binder 機制 1/3:驅動篇[17]
  2. 理解 Android Binder 機制 2/3:C++層[18]
  3. 理解 Android Binder 機制 3/3:Java 層[19]

附件

本文涉及到的附件也上傳了,各位下載后解壓,使用 「Chrome」 瀏覽器打開即可點此鏈接下載文章所涉及到的 Systrace 附件[20]

Reference

[1]Systrace 簡介: https://www.androidperformance.com/2019/05/28/Android-Systrace-About/

[2]Systrace 基礎知識 - Systrace 預備知識: https://www.androidperformance.com/2019/07/23/Android-Systrace-Pre/

[3]Systrace 基礎知識 - Why 60 fps ?: https://www.androidperformance.com/2019/05/27/why-60-fps/

[4]Systrace 基礎知識 - SystemServer 解讀: https://www.androidperformance.com/2019/06/29/Android-Systrace-SystemServer/

[5]Systrace 基礎知識 - SurfaceFlinger 解讀: https://www.androidperformance.com/2020/02/14/Android-Systrace-SurfaceFlinger/

[6]Systrace 基礎知識 - Input 解讀: https://www.androidperformance.com/2019/11/04/Android-Systrace-Input/

[7]Systrace 基礎知識 - Vsync 解讀: https://www.androidperformance.com/2019/12/01/Android-Systrace-Vsync/

[8]Systrace 基礎知識 - Vsync-App :基于 Choreographer 的渲染機制詳解: https://androidperformance.com/2019/10/22/Android-Choreographer/

[9]Systrace 基礎知識 - MainThread 和 RenderThread 解讀: https://www.androidperformance.com/2019/11/06/Android-Systrace-MainThread-And-RenderThread/

[10]Systrace 基礎知識 - Binder 和鎖競爭解讀: https://www.androidperformance.com/2019/12/06/Android-Systrace-Binder/

[11]Systrace 基礎知識 - Triple Buffer 解讀: https://www.androidperformance.com/2019/12/15/Android-Systrace-Triple-Buffer

[12]Systrace 基礎知識 - CPU Info 解讀: https://www.androidperformance.com/2019/12/21/Android-Systrace-CPU

[13]理解 Android Binder 機制 1/3:驅動篇: https://paul.pub/android-binder-driver/

[14]理解 Android Binder 機制 2/3:C++層: https://paul.pub/android-binder-cpp/

[15]理解 Android Binder 機制 3/3:Java 層: https://paul.pub/android-binder-java/

[16]Systrace 中查看進程信息喚醒: https://www.androidperformance.com/2019/07/23/Android-Systrace-Pre/#%E8%BF%9B%E7%A8%8B%E5%94%A4%E9%86%92%E4%BF%A1%E6%81%AF%E5%88%86%E6%9E%90

[17]理解 Android Binder 機制 1/3:驅動篇: https://paul.pub/android-binder-driver/

[18]理解 Android Binder 機制 2/3:C++層: https://paul.pub/android-binder-cpp/

[19]理解 Android Binder 機制 3/3:Java 層: https://paul.pub/android-binder-java/

[20]點此鏈接下載文章所涉及到的 Systrace 附件: https://github.com/Gracker/SystraceForBlog/tree/master/Android_Systrace_Binder

[21]關于我: https://www.androidperformance.com/about/

[22]博客內容導航: https://androidperformance.com/2019/12/01/BlogMap/

[23]優秀博客文章記錄 - Android 性能優化必知必會: https://androidperformance.com/2018/05/07/Android-performance-optimization-skills-and-tools/


https://mp.weixin.qq.com/s/VQteDCB9rRjhLDl3MADHlg

Android Systrace 基礎知識(11) - Triple Buffer 解讀

發布于:3月以前  |  499次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(7) - Vsync 解讀

發布于:3月以前  |  521次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(6) - Input 解讀

發布于:3月以前  |  505次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(5) - SurfaceFlinger 解讀

發布于:3月以前  |  521次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(4) - SystemServer 解讀

發布于:3月以前  |  481次閱讀  |  詳細內容 ?

Android Systrace 基礎知識(3) - Why 60 fps ?

發布于:3月以前  |  424次閱讀  |  詳細內容 ?

Android Systrace 基礎知識 -- Systrace 簡介

發布于:3月以前  |  491次閱讀  |  詳細內容 ?

所屬標簽

最多閱讀

簡化Android的UI開發 2年以前  |  515111次閱讀
Android 深色模式適配原理分析 1年以前  |  26409次閱讀
Android 樣式系統 | 主題背景覆蓋 1年以前  |  7949次閱讀
Android Studio 生成so文件 及調用 1年以前  |  5577次閱讀
30分鐘搭建一個android的私有Maven倉庫 3年以前  |  4746次閱讀
Android設計與開發工作流 2年以前  |  4407次閱讀
Google Enjarify:可代替dex2jar的dex反編譯 3年以前  |  4390次閱讀
Android多渠道打包工具:apptools 3年以前  |  4023次閱讀
移動端常見崩潰指標 2年以前  |  3990次閱讀
Google Java編程風格規范(中文版) 3年以前  |  3938次閱讀
Android-模塊化-面向接口編程 1年以前  |  3849次閱讀
Android內存異常機制(用戶空間)_NE 1年以前  |  3813次閱讀
Android UI基本技術點 3年以前  |  3786次閱讀
Android死鎖初探 2年以前  |  3730次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>