SystemServiceRegistry启动&zygote加载类

背景

调查SystemServiceRegistry的启动流程,查看到日志,SystemServiceRegistryImpl是在zygote64进程的。

09-01 13:59:51.197488   429   429 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<
09-01 13:59:51.474706   430   430 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<
	
09-01 13:59:52.034022   429   429 I SystemServiceRegistry: reflect pass cls:SystemServiceRegistryImpl   -- 在SystemServiceRegistry静态方法块中添加的日志。
09-01 13:59:55.574959   430   430 I SystemServiceRegistry: reflect pass cls:SystemServiceRegistryImpl

再次查看调用的上下文,是zogte在加载类的时候

09-01 13:59:51.912956   429   429 D Zygote  : begin preload
09-01 13:59:51.913001   429   429 I Zygote  : Calling ZygoteHooks.beginPreload()
09-01 13:59:51.964805   429   429 D Zygote64Timing: BeginPreload took to complete: 52ms
09-01 13:59:51.965014   429   429 I Zygote  : Preloading classes...
09-01 13:59:51.966624   429   429 W Zygote  : Class not found for preloading: android.app.-$$Lambda$ResourcesManager$QJ7UiVk_XS90KuXAsIjIEym1DnM
09-01 13:59:51.997728   429   512 I zygote64: Verified 1383 classes from mainline modules in 255.471ms
09-01 13:59:52.034022   429   429 I SystemServiceRegistry: reflect pass cls:SystemServiceRegistryImpl

定位到代码:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java #preloadClasses

加载常用类–提前替其他进程加载,因为都是从zygote64 fork出去的,zygote64 加载后,再fork,fork后的进程就不需要再单独加载了。

    /**
     * Performs Zygote process initialization. Loads and initializes commonly used classes.
     * Most classes only cause a few hundred bytes to be allocated, but a few will allocate a dozen Kbytes (in one case, 500+K).
     */
    private static void preloadClasses() {
        final VMRuntime runtime = VMRuntime.getRuntime();
        InputStream is;
        **is = new FileInputStream(PRELOADED_CLASSES);**

        Log.i(TAG, "Preloading classes...");
        long startTime = SystemClock.uptimeMillis();
        。。。 。。。

        try {
            BufferedReader br =
                    new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);

            int count = 0;
            int missingLambdaCount = 0;
            String line;
            while ((line = br.readLine()) != null) {
                // Skip comments and blank lines.
                line = line.trim();
                if (line.startsWith("#") || line.equals("")) {
                    continue;
                }

                Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
                try {
                    // Load and explicitly initialize the given class. Use
                    // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
                    // (to derive the caller's class-loader). Use true to force initialization, and
                    // null for the boot classpath class-loader (could as well cache the
                    // class-loader of this class in a variable).
                    Class.forName(line, true, null); //static native Class<?> classForName(String className, boolean shouldInitialize, ClassLoader classLoader)
                    count++;
                } catch (ClassNotFoundException e) {
                    。。。 。。。
            }

            Log.i(TAG, "...preloaded " + count + " classes in "
                    + (SystemClock.uptimeMillis() - startTime) + "ms.");
            if (LOGGING_DEBUG && missingLambdaCount != 0) {
                Log.i(TAG, "Unresolved lambda preloads: " + missingLambdaCount);
            }
        } catch (IOException e) {
            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
        } finally {
            IoUtils.closeQuietly(is);

            // Fill in dex caches with classes, fields, and methods brought in by preloading.
            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadDexCaches");
            runtime.preloadDexCaches();
            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
           。。。 。。。
        }
    }

1.将PRELOADED_CLASSES = "/system/etc/preloaded-classes"中记录的类按行读取出来,并且判空。

private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
 line = line.trim();
 if (line.startsWith("#") || line.equals("")) {
      continue;
 }

preloaded-classes部分展示

# Preloaded-classes filter file for phones.
#
# Classes in this file will be allocated into the boot image, and forcibly initialized in
# the zygote during initialization. This is a trade-off, using virtual address space to share
# common heap between apps.
#
# This file has been derived for mainline phone (and tablet) usage.
#
android.R$styleable
android.accessibilityservice.AccessibilityServiceInfo$1
android.accessibilityservice.AccessibilityServiceInfo
android.accessibilityservice.IAccessibilityServiceClient$Stub$Proxy
android.accessibilityservice.IAccessibilityServiceClient$Stub
android.accessibilityservice.IAccessibilityServiceClient
android.accounts.AbstractAccountAuthenticator$Transport

Class.forName加载类到内存并且初始化

Class.forName(line, true, null); 
//static native Class<?> classForName(String className, boolean shouldInitialize, ClassLoader classLoader)

tips:Class.forName作用

  1. 加载: 当您调用 **Class.forName("ClassName")*时,负责调用代码的 Java ClassLoader 会尝试将名为 "ClassName "的类加载到 Java 虚拟机(JVM)中。

    加载时,会去查找classpath指定的路径中的jar文件,匹配className。

  2. 链接: 类加载完成后,会经历链接过程,该过程包括验证类、为类变量分配内存以及其他必要的准备工作。

  3. 初始化: 默认情况下,Class.forName() 也会对类进行初始化,即处理所有静态块和静态变量。这一步至关重要,因为它为类在 JVM 中的使用做好了准备。

其他问题

SystemServiceRegistry中只在zygote中加载了,其他进程又是如何访问的呢?

由于java中不存在内存共享机制(类似c++中so文件),各个进程如果依赖同一个jar包,都是独立使用自己的类加载器将jar包加载到内存空间的。

从加的日志上看,SystemServiceRegistry只在zygote中加载了(zygote以及zygote64),但是各个应用进程都间接的调用到SystemServiceRegistry获取到Manager,这个是怎么实现的呢?

主要还是fork机制,父线程fork,子线程会继承父线程的内存空间,因此当zygote加载了SystemServiceRegistry后,其他被zygote fork出来的进程也自动获得了SystemServiceRegistry。

SystemServiceRegistry启动早于系统服务,如何能够获取系统服务呢?

SystemServiceRegistry中,getSystemService的逻辑许多都是通过ServiceManager获取服务的binder代理,通过Stub.asInterface转成接口,然后通过XXXManager的构造方法,注入进去,最后返回给应用。

但是目前发现SystemServiceRegistry的加载是在系统服务启动前,如何解决呢?

SystemServiceRegistry的加载中只是设定了系统服务名字以及服务的获取方式的匹配,并不会真的执行,所以也不会立即获取服务binder。只是在getSystemService的时候,才会去获取binder,所以并不存在矛盾。

zygote中预加载class和资源的好处

  1. 节省时间:

    因为zygote加载的class和resource是通用的,其他进程都会用到的。

    当zygote去fork进程的时候,这些都会被拷贝到自己的内存空间中,新的进程不用单独再加载一次,加快新进程的启动。

    根据fork的copy-on-write机制可知,有些类如果不做改变,甚至都不用复制,子进程可以和父进程共享这部分数据,从而省去不少内存的占用。

    在这里插入图片描述

  2. 减少冗余代码

    新进程不用单独再去加载class和资源,减少了冗余代码

当前流程

zygote-java层的启动
在这里插入图片描述

孵化应用进程这种事为什么不交给SystemServer来做,而专门设计一个Zygote?

可以应用父类子类的观点来解释。

把子类的共性都抽离出来,做成父类的基本属性,这样子类就不用单独声明这些属性,就能拥有他们。

以这个角度看待孵化应用进程。

如果是SystemServer孵化应用进程的话,也就是SystemServer作为父类,其他应用进程作为子类。

如果是zygote孵化应用进程的话,也就是zygote作为父类,其他应用进程作为子类。

那么问题就变成了SystemServer和zygote相比,谁更适合做为父类?

SystemServer和zygote相比,多了很多系统服务,这些肯定不是应用进程的“属性”,所以相比 而言,zygote会更加的纯粹,更能集中体现各个应用进程的“通性”。

  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值