一个应用进程里有多少线程,谁决定进程生死
一个应用进程里有多少线程,谁决定进程生死
看到主线程卡住,不能推断整个进程里没有其他代码在运行。一个普通应用进程至少同时存在主 线程、Binder 工作线程,以及应用自行创建的线程;它们共享进程资源,却有不同的调度入口。 进程是否在内存压力下被回收,则由 system_server 的优先级计算与 lmkd 的执行共同决定。
主线程为何能执行组件回调
应用进程进入 Java 层后,从 ActivityThread.main() 准备主 Looper 并进入消息循环。Activity、 Service、Receiver 等组件回调最终由这条线程执行,所以耗时回调会阻塞 UI 和后续生命周期 消息。
系统向应用发起生命周期事务时,请求先经 Binder 到达应用进程。ApplicationThread 是应用 暴露给系统侧的 Binder 接口,它接收请求后把工作转成主线程消息。这里包含一次明确的线程 切换:Binder 工作线程负责收件,ActivityThread.H 所在的主线程负责真正修改组件状态。
这也解释了一个常见现场:Binder 请求已经到达应用,但 onCreate() 迟迟没有开始。如果主 线程前面压着耗时消息,生命周期事务只能继续排队。
Binder 工作线程不等于后台业务线程
Binder 工作线程在 Native Binder 运行时中进入事务循环,从驱动取出发给本进程的命令并执行 服务端分发。核心入口可从 frameworks/native/libs/binder/IPCThreadState.cpp 的 joinThreadPool() 和 getAndExecuteCommand() 开始阅读。
线程池容量不是无限的。大量同步 Binder 请求长时间占住工作线程时,后续事务可能等待可用 线程。排查“跨进程调用卡住”时,应同时看调用方等待栈和服务端 Binder 线程栈,不能只盯着 主线程。
应用自行创建的线程池、Thread 或 HandlerThread 属于另一类执行单元。它们可以处理耗时 工作,但不会因此获得操作 View 的权限;要更新 UI,仍需把结果投递回主线程。
android:process 创建的是另一个进程
进程是地址空间、UID 身份、文件描述符和内存统计的容器;线程是容器内可调度的执行流。 Manifest 中的 android:process 会让组件运行在另一个 Linux 进程,而不是多创建一条线程。 新进程有自己的 ActivityThread、主 Looper、Binder 状态和进程优先级记录,进程间数据仍需 通过 Binder、文件或其他 IPC 传递。
这也是静态单例在多进程应用中失效的原因:每个进程都有独立地址空间,各自持有一份实例。
谁给进程排序,谁执行回收
Android 14 中,AMS 为每个应用进程维护 ProcessRecord,OomAdjuster 根据前台组件、可见性、 服务绑定和进程依赖计算优先级,并把结果落实为 Linux oom_score_adj。常用分档定义位于 frameworks/base/services/core/java/com/android/server/am/ProcessList.java。
oom_score_adj 示例 | 含义 |
|---|---|
0 | 典型前台应用进程 |
100 | 典型可见进程 |
700 | previous app 分档 |
900 起 | 缓存进程范围 |
数值越大,进程在内存压力下越容易成为回收候选。不过,“当前值是 900”不等于进程会立刻 被杀;lmkd 还要结合系统内存压力和候选进程状态作出动作。反过来,只改应用自身逻辑也不能 固定优先级,因为组件状态和依赖变化会触发后续重新计算。
如何观察现场
在连接调试设备的主机终端执行:
adb shell pidof com.example.demo
adb shell ps -T -p <PID>
adb shell cat /proc/<PID>/oom_score_adj
adb shell dumpsys activity processesps -T 用于核对线程列表和 TID;线程名只能帮助初筛,最终仍要结合堆栈判断它在做什么。 oom_score_adj 是采样瞬间的内核值,切换前后台后可能很快变化。dumpsys activity processes 提供 AMS 侧记录,但字段随版本和厂商实现变化,分析时要保存完整原始输出。
如果现象是 UI 无响应,先看主线程正在执行哪个消息;如果同步 Binder 调用长时间不返回, 同时检查两端 Binder 线程;如果进程消失,则把退出原因、oom_score_adj、内存压力和 AMS 状态放在同一时间窗口里。三类问题不能用“线程卡了”一个结论代替。
源码入口
主线程从 frameworks/base/core/java/android/app/ActivityThread.java 进入;Binder 工作线程 看 frameworks/native/libs/binder/IPCThreadState.cpp;进程分档和计算分别从 ProcessList.java、OomAdjuster.java 进入。