工厂背景:ART 虚拟机的日常运作
想象 Android 系统是一个大型工厂,每个应用都是工厂里的独立车间,而 ART 虚拟机就是管理这些车间运作的智能控制系统。车间里有很多工人(线程)在忙碌,有的处理用户点击(主线程),有的搬运数据(Binder 线程),还有的清理垃圾(GC 线程)。
这个工厂有个特殊规定:当车间出现异常(比如工人长时间不响应),可以发送一个 "紧急报告" 信号(SIGQUIT,编号 3),让管理层生成一份详细的工作状态报告,这就是我们说的 Trace 文件。
第一章:门卫 SignalCatcher 的紧急响应
在 ART 工厂门口,有个叫 SignalCatcher 的门卫(位于 signal_catcher.cc),他的主要工作是监听外部信号:
cpp
运行
// SignalCatcher.cc: 门卫的工作流程
void* SignalCatcher::Run(void* arg) {
// 初始化工作环境
SignalSet signals;
signals.Add(SIGQUIT); // 关注紧急报告信号
signals.Add(SIGUSR1); // 其他辅助信号
while (true) {
int signal_number = WaitForSignal(...); // 监听信号
switch (signal_number) {
case SIGQUIT:
HandleSigQuit(); // 收到紧急报告,开始处理
break;
// 其他信号处理...
}
}
}
某天,管理员通过命令 "adb shell kill -3 888" 向车间 888 发送了紧急报告信号,门卫 SignalCatcher 立刻捕获到这个信号,开始执行紧急响应流程。
第二章:厂长 Runtime 的全局调度
门卫通知了工厂总经理 Runtime(位于 runtime.cc),总经理立即启动全厂状态收集流程:
cpp
运行
// runtime.cc: 总经理的调度指令
void Runtime::DumpForSigQuit(std::ostream& os) {
GetClassLinker()->DumpForSigQuit(os); // 让类加载部门提交报告
GetInternTable()->DumpForSigQuit(os); // 让字符串管理部门提交报告
GetJavaVM()->DumpForSigQuit(os); // 让JNI部门提交报告
GetHeap()->DumpForSigQuit(os); // 让内存管理部门提交报告
thread_list_->DumpForSigQuit(os); // 让所有工人队伍提交工作状态
// 其他部门报告...
}
2.1 类加载部门的报告(ClassLinker)
类加载部门主管(位于 class_linker.cc)报告:
"我们从 Zygote 老厂继承了 4113 个类,建厂后又新增了 3239 个类,目前总共管理 7352 个类。"
cpp
运行
// class_linker.cc: 类加载报告
void ClassLinker::DumpForSigQuit(std::ostream& os) {
os << "Zygote loaded classes=" << pre_zygote_class_table_.Size()
<< " post zygote classes=" << class_table_.Size() << "\n";
}
2.2 字符串管理部门的报告(InternTable)
字符串管理部门主管(位于 intern_table.cc)报告:
"我们管理着 57550 个常用字符串(强引用)和 9315 个临时字符串(弱引用),方便工人们快速查找。"
cpp
运行
// intern_table.cc: 字符串管理报告
void InternTable::DumpForSigQuit(std::ostream& os) const {
os << "Intern table: " << StrongSize() << " strong; " << WeakSize() << " weak\n";
}
2.3 内存管理部门的报告(Heap)
内存管理部门(位于 heap.cc)展示仓库状态:
"仓库总容量 40MB,已用 29MB(占 63%),目前存放了 207772 个物品(对象),近期垃圾清理情况如下...(GC 信息省略)"
cpp
运行
// heap.cc: 内存状态报告
void Heap::DumpForSigQuit(std::ostream& os) {
os << "Heap: " << GetPercentFree() << "% free, "
<< PrettySize(GetBytesAllocated()) << "/" << PrettySize(GetTotalMemory())
<< "; " << GetObjectsAllocated() << " objects\n";
DumpGcPerformanceInfo(os); // 详细GC报告
}
第三章:工人队伍(线程)的工作状态普查
总经理最关心的是工人们的工作状态,线程管理部门(位于 thread_list.cc)开始逐一检查:
cpp
运行
// thread_list.cc: 工人队伍普查
void ThreadList::DumpForSigQuit(std::ostream& os) {
os << "DALVIK THREADS (" << list_.size() << "):\n"; // 先报总数
// 逐个工人检查
for (每个线程) {
Thread::DumpState(os, 线程, tid); // 检查工作状态
DumpKernelStack(os, tid, " kernel: ", false); // 查看在工厂外的工作记录
}
}
3.1 主线程的工作记录
主线程(main)是车间的核心负责人,他的工作记录显示:
"我正在处理消息队列(MessageQueue.nativePollOnce),最近在 Looper.loop 循环中协调各部门工作,目前处于等待状态(state=S)。"
plaintext
"main" prio=5 tid=1 Native
| sysTid=12078 nice=-2 // 工号12078,优先级较高
| state=S // 正在休息(等待消息)
| schedstat=(5907843636 827600677 5112) // 工作5907ms,等待827ms,切换5112次
| stack=0x7fd64ef000-0x7fd64f1000 // 工作空间地址
// 工作调用链(从内核到Java层)
kernel: __switch_to+0x70/0x7c
native: #05 pc ... Java_android_os_MessageQueue_nativePollOnce...
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.Looper.loop(Looper.java:135)
...
3.2 Binder 线程的工作记录
Binder 线程是车间之间的快递员,他的记录显示:
"我正在等待其他车间的快递任务(binder_thread_read),目前处于阻塞状态(state=S),已经处理了 50ms 用户态工作和 29ms 内核态工作。"
plaintext
"Binder_1" prio=5 tid=8 Native
| sysTid=12092 nice=0 // 工号12092,普通优先级
| state=S // 等待快递任务
| utm=50 stm=29 // 用户态工作500ms,内核态290ms
| stack=0x7fa2647000-0x7fa2649000 stackSize=1013KB // 工作空间
// 内核工作记录
kernel: binder_thread_read+0xd78/0xeb0
// 本地方法调用链
native: #02 pc ... _ZN7android14IPCThreadState14talkWithDriverEb+164
(no managed stack frames) // 没有Java层工作
第四章:报告生成与保存
所有部门的报告汇总后,由门卫 SignalCatcher 统一整理成完整的 Trace 文件,保存到工厂的公告栏(/data/anr/traces.txt):
cpp
运行
// signal_catcher.cc: 报告保存
void SignalCatcher::Output(const std::string& s) {
int fd = open("/data/anr/traces.txt", O_APPEND | O_CREAT | O_WRONLY, 0666);
// 写入报告内容
file->WriteFully(s.data(), s.size());
LOG(INFO) << "Wrote stack traces to '/data/anr/traces.txt'";
}
最终生成的报告开头如下:
plaintext
----- pid 888 at 2016-11-11 22:22:22 -----
Cmd line: system_server // 车间名称
ABI: arm // 工人使用的工具类型
Build type: optimized // 工厂生产模式
Zygote loaded classes=4113 post zygote classes=3239 // 类加载情况
Intern table: 57550 strong; 9315 weak // 字符串管理
Libraries: /system/lib/libandroid.so ... (16) // 依赖的工具库
Heap: 27% free, 29MB/40MB; 307772 objects // 内存使用
DALVIK THREADS (99): // 总共有99个工人
故事背后的调试价值
当工厂出现 ANR(工人长时间不响应)时,这份 Trace 报告就是关键的诊断工具:
-
通过主线程的堆栈(Looper.loop)可以判断是否卡在消息处理中
-
查看各线程的 state 状态(S = 睡眠,R = 运行,D = 阻塞)判断是否有死锁
-
schedstat 数据能分析线程的 CPU 占用情况(utm+stm = 总工作时间)
-
内核栈信息可以定位到 Linux 底层的阻塞点(如 epoll_wait、binder_ioctl)
通过这个故事,我们理解了 ART 处理 Trace 信号的完整流程:从信号捕获(SignalCatcher)到全局调度(Runtime),再到各模块数据收集,最终生成包含进程状态、内存使用、线程堆栈的详细报告,这些信息是分析 Android 应用卡顿、ANR 问题的重要依据。