背景
Java 多线程开发中为了保证数据的一致性,引入了同步锁(synchronized)。但是,对锁的过度使用,可能导致卡顿问题,甚至 ANR:
-
Systrace 中的主线程因为等锁阻塞了绘制,导致卡顿
-
Slardar 平台(字节跳动内部 APM 平台,以下简称 Slardar)中搜索 waiting to lock 关键字发现很多锁导致的 ANR,仅 Java 锁异常占到总 ANR 的 3.9%
本文将着重向大家介绍 Slardar 线上锁监控方案的原理与使用方法,以及我们在抖音上发现的锁的经典案例与优化实践。
监控方案
获取运行时锁信息的方法有以下几种
方案 | 应用范围 | 特点 |
---|---|---|
systrace | 线下 |
|
定制 ROM | 线下 |
|
JVMTI | 线下 |
|
考虑到,很多锁问题需要一定规模的线上用户才能暴露出来,另外没有调用栈难以从根本上定位和解决线上用户的锁问题。最终我们自研了一套线上锁监控系统,它需要满足以下要求:
-
线上监控方案
-
丰富的锁信息,包括 Java 调用栈
-
数据分析平台,包括聚合能力,设备和版本信息等
-
可纳入开发和合码流程,防止不良代码上线
这样的锁监控系统,能够帮助我们高效定位和解决线上问题,并实现防劣化。
锁监控原理
我们先从 Systrace 入手,有一类常见的耗时叫做 monitor contention,其实是 Android ART 虚拟机输出的锁信息。
简单介绍一下里面的信息
monitor contention with owner work_thread (27176) at android.content.res.Resources android.app.ResourcesManager.getOrCreateResources(android.os.IBinder, android.content.res.ResourcesKey, java.lang.ClassLoader)(ResourcesManager.java:901) waiters=1 blocking from java.util.ArrayList android.app.ActivityThread.collectComponentCallbacks(boolean, android.content.res.Configuration)(ActivityThread.java:5836)
-
持锁线程:work_thread
-
持锁线程方法:android.app.ResourcesManager.getOrCreateResources(...)
-
等待线程 1 个
-
等锁方法:android.app.ActivityThread.collectComponentCallbacks(...)
Java 锁,无论是同步方法还是同步块,虚拟机最终都会到 MonitorEnter。我们关注的 trace 是 Android 6 引入的, 在锁的开始和结束时分别调用 ATRACE_BEGIN(...)
和 ATRACE_END()