Skip to content

服务启动耗时

追踪 TimingsTraceAndSlog、SystemServiceManager 耗时告警、初始化线程池异步 trace 和 TotalBootTime。

基于android-17.0.0_r1
AndroidSystemServerPerfetto性能源码阅读

服务启动耗时 ​

SystemServer 启动慢不能只看一条 SystemServerTiming 日志。Android 17 同时提供四层时间信息:TimingsTraceAndSlog 记录主线程嵌套阶段;SystemServiceManager.warnIfTooLong() 测量单个生命周期回调;SystemServerInitThreadPool 为并行任务建立异步 trace 和 pending task;BootCompleted 阶段记录总启动时间并关闭线程池。只有把四层对齐,才能区分“主线程自身慢”“等待 Future”“单服务 onStart 慢”和“并行任务占用资源拖慢关键路径”。

1. 主线程trace层次 ​

源码文件:frameworks/base/services/java/com/android/server/SystemServer.java

java
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
    t.traceBegin("InitBeforeStartServices");
    ...
} finally {
    t.traceEnd();
}

try {
    t.traceBegin("StartServices");
    startBootstrapServices(t);
    startCoreServices(t);
    startOtherServices(t);
    startApexServices(t);
} finally {
    t.traceEnd();
}

顶层 section 把服务前准备和服务启动分开;各组内部继续嵌套 StartInstaller、StartPackageManagerService、StartWindowManagerService 等。Perfetto 中父 section 耗时包含子 section,不能把父子耗时相加,否则会重复计算。

2. 服务启动计时 ​

源码文件:frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

java
public <T extends SystemService> T startService(
        Class<T> serviceClass) {
    try {
        String name = serviceClass.getName();
        Trace.traceBegin(
                Trace.TRACE_TAG_SYSTEM_SERVER,
                "StartService " + name);
        T service = serviceClass
                .getConstructor(Context.class)
                .newInstance(mContext);
        startService(service);
        return service;
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
    }
}

public void startService(SystemService service) {
    long time = SystemClock.elapsedRealtime();
    service.onStart();
    warnIfTooLong(
            SystemClock.elapsedRealtime() - time,
            service, "onStart");
}

StartService <class> 覆盖反射构造和 onStart();warnIfTooLong 只测 onStart() 部分。因此 trace 长而 warning 不出现时,应检查构造器、静态初始化或 classloader;warning 出现则直接回到 onStart() 的发布、HAL 等待或磁盘 I/O。

3. 慢调用告警 ​

java
private void warnIfTooLong(long duration,
        SystemService service, String operation) {
    if (duration > SERVICE_CALL_WARN_TIME_MS) {
        Slog.w(TAG,
                "Service " + service.getClass().getName()
                + " took " + duration + " ms in "
                + operation);
    }
}

告警是阈值检测,不会中止启动,也不证明服务发生死锁。duration 使用 elapsed realtime,不含设备 suspend 的时钟语义影响较少。相同 helper 也用于 onBootPhase 和用户生命周期,必须读 operation 字段才能知道慢在哪个 callback。

4. BootPhase耗时 ​

java
long time = SystemClock.elapsedRealtime();
t.traceBegin("OnBootPhase_" + phase + "_"
        + service.getClass().getName());
service.onBootPhase(mCurrentPhase);
warnIfTooLong(
        SystemClock.elapsedRealtime() - time,
        service, "onBootPhase");
t.traceEnd();

串行 callback 直接形成主线程嵌套 section。并行 callback 则提交线程池,Future 全部完成后 phase 才结束;一个慢并行服务会表现为 phase section 等待,但主线程 CPU 可能很低。

5. 初始化线程池 ​

源码文件:frameworks/base/services/core/java/com/android/server/SystemServerInitThreadPool.java

java
private <T> Future<T> submitTask(
        Callable<T> callable, String description) {
    synchronized (mPendingTasks) {
        Preconditions.checkState(!mShutDown,
                TAG + " already shut down");
        mPendingTasks.add(description);
    }
    return mService.submit(() -> {
        TimingsTraceAndSlog traceLog =
                TimingsTraceAndSlog.newAsyncLog();
        traceLog.traceBegin(
                "InitThreadPoolExec:" + description);
        try {
            return callable.call();
        } finally {
            synchronized (mPendingTasks) {
                mPendingTasks.remove(description);
            }
            traceLog.traceEnd();
        }
    });
}

任务提交时加入 pending list,执行线程创建独立 async trace;完成后移除。调用方拿到 Future,必须在依赖点等待。Perfetto 中应使用 description 把 submit、InitThreadPoolExec 和 Future.get/waitForFutureNoInterrupt 对齐。

6. 长任务与Watchdog ​

源码文件:frameworks/base/services/java/com/android/server/SystemServer.java

java
Watchdog.getInstance().pauseWatchingCurrentThread(
        "packagemanagermain");
try {
    mPackageManagerService = PackageManagerService.main(...);
} finally {
    Watchdog.getInstance().resumeWatchingCurrentThread(
            "packagemanagermain");
}

PMS 是已知长任务,暂停的是当前线程 checker,不是关闭 Watchdog。耗时分析仍应使用 trace;pause 只防止合法长初始化触发误杀。若 pause 后没有 resume,Watchdog 保护会被削弱,因此 finally 是正确性边界。

7. 总启动时间 ​

源码文件:frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

java
if (phase == SystemService.PHASE_BOOT_COMPLETED) {
    long totalBootTime = SystemClock.uptimeMillis()
            - mRuntimeStartUptime;
    t.logDuration("TotalBootTime", totalBootTime);
    shutdownInitThreadPool();
}

总耗时从 SystemServer 构造时记录的 runtime start uptime 到 BootCompleted phase;它不是从开机上电计算,也不含之后用户交互任务。线程池在该阶段关闭,之后再 submit 会抛 IllegalStateException,避免“启动线程池”演变成长期后台执行器。

8. 可执行采集方法 ​

bash
# 源码定位:阶段 trace、服务 trace 和线程池任务描述。
rg -n "TimingsTraceAndSlog|traceBegin\(|StartService|warnIfTooLong|InitThreadPoolExec|TotalBootTime" \
  frameworks/base/services/java/com/android/server/SystemServer.java \
  frameworks/base/services/core/java/com/android/server/SystemServiceManager.java \
  frameworks/base/services/core/java/com/android/server/SystemServerInitThreadPool.java

# 运行时日志:输入 system_server 日志,输出慢生命周期告警和启动阶段。
adb logcat -b system -v threadtime \
  'SystemServerTiming:*' 'SystemServiceManager:*' 'SystemServerInitThreadPool:*' '*:S'

# Perfetto:采集 sched、freq、binder、dalvik 与 system_server atrace,
# 再按 StartService/InitThreadPoolExec/OnBootPhase 名称关联。
adb shell perfetto -o /data/misc/perfetto-traces/system-server-start.pftrace \
  -t 30s sched freq binder_driver dalvik am wm

命令是否可用取决于设备构建、权限和 Perfetto 配置;没有 trace 权限时至少保留 SystemServerTiming、SystemServiceManager warning 和线程池 pending dump。

9. 反优化边界 ​

  • 把任务移到线程池可能缩短主线程 section,却增加锁竞争、GC 或后续 Future 等待;
  • 缩短 onStart() 不应通过延后发布必须立即可用的 Binder/LocalServices;
  • 提高 Watchdog timeout 不会优化启动,只会推迟故障发现;
  • 删除预加载可能让 SystemServer 更快,却把成本复制到每个应用进程;
  • 只比较 TotalBootTime 会掩盖某个阶段回归和并行资源竞争。

下一篇将分析 Trampoline/Lifecycle 包装模式如何影响启动 trace、实例 owner 和延迟初始化;不会重复本篇的耗时采集方法。