Android 虚拟机与类加载机制

Android 虚拟机与类加载机制

虚拟机

  • JVM虚拟机
  • 是Java中的虚拟机它的指令集是基于堆栈的,运行的是每一个.class
  • Dalvik虚拟机
  • 是Android中的虚拟机它的指令集是基于寄存器,运行的是所有java字节码通过dx打包工具打成.dex,在Android2.2开始支持JIT及时编译(Just In Time),在程序运行中将热点代码(经常使用)进行优化或者编译.
  • ART虚拟机
  • ART虚拟机是Android4.4中的一个开发者选项,在5.0中是默认虚拟机.ART引用了一个AOT预先编译机制(Ahead Of Time),在应用安装时使用dex2oat工具将dex字节码编译成机器直接使用的机器码,所以在5.0后会感觉感觉安装应用会慢些.但是在Android N就采用了混合编译,不在安装时进行AOT,而是运行时采用JIT将常用方法记录在Profile中.当系统空闲或充电时,编译守护进程会运行,将Profile中常用的代码进行AOT编译,下次运行时直接使用.

Android中ClassLoader

  • PathClassLoader与DexClassLoader区别
  • 都是可以加载外部的dex/apk,只不过DexClassLoader可以指定optimizedDirectory,也就是.odex存放的位置
  • BaseDexClassLoader 构造
  public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        this(dexPath, librarySearchPath, parent, null, false);
    }

BaseDexClassLoaderchan参数的含义

  • dexPath 需要加载的文件列表,文件可以是包含了 classes.dex 的 JAR/APK/ZIP,也可以直接使用 classes.dex 文件,多个文件用 “:” 分割
  • librarySearchPath 存放需要加载的 native 库的目录
  • optimizedDirectory 存放优化后的 dex,可以为空
  • parent 父 ClassLoader
    通过构造可以知道通过传入的dex文件,然后优化后保存在optimizedDirectory目录
  • PathClassLoader 构造
public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }
    
 public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }

通过类的注释可以知道它是系统类也是其它应用程序类的加载器,通过构造可以发现传入optimizedDirectory参数为null

  • DexClassLoader 构造 8.0之前与8.0是有所区别的,之前是一个给创建optimizedDirectory参数,一个没有optimizedDirectory参数,所以说8.0之后DexClassLoader与PathClassLoader没有区别了
  public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
    }
 public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
  • 类加载流程
  • 首先类加载是双亲委派 看下ClassLoader代码
protected Class<?> loadClass(String name, boolean resolve)
       throws ClassNotFoundException
   {
           // First, check if the class has already been loaded
           //检查Class是否被加载
           Class<?> c = findLoadedClass(name);
           if (c == null) {
               try {
                   if (parent != null) {
                   	//如果parent不为空,则用parent的loadClass加载
                       c = parent.loadClass(name, false);
                   } else {
                   	//parent为空则用BootClassLoader加载
                       c = findBootstrapClassOrNull(name);
                   }
               } catch (ClassNotFoundException e) {
                   // ClassNotFoundException thrown if class not found
                   // from the non-null parent class loader
               }

               if (c == null) {
                   // If still not found, then invoke findClass in order
                   // to find the class.
                   //如果都找不到就自己加载
                   c = findClass(name);
               }
           }
           return c;
   }
  • 这么做的好出就是

    • 避免重复加载,当父类加载器已经加载了该类,就没必要子类ClassLoader在加载一次.
    • 安全性考虑,防止核心API库被随意篡改.
  • 再说一下Android类加载流程

    • 首先会走findClass,但是PathClassLoader中没有这个方法会走到BaseDexClassLoader中
     @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
       // First, check whether the class is present in our shared libraries.
       if (sharedLibraryLoaders != null) {
           for (ClassLoader loader : sharedLibraryLoaders) {
               try {
                   return loader.loadClass(name);
               } catch (ClassNotFoundException ignored) {
               }
           }
       }
       // Check whether the class in question is present in the dexPath that
       // this classloader operates on.
       List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
       Class c = pathList.findClass(name, suppressedExceptions);
       if (c == null) {
           ClassNotFoundException cnfe = new ClassNotFoundException(
                   "Didn't find class \"" + name + "\" on path: " + pathList);
           for (Throwable t : suppressedExceptions) {
               cnfe.addSuppressed(t);
           }
           throw cnfe;
       }
       return c;
    }
    

    可以看到会从 pathList.findClass(name, suppressedExceptions)找,这个pathList有是什么他是从构造中创建的就是DexPathList对象

        public BaseDexClassLoader(String dexPath,
            String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
            boolean isTrusted) {
        super(parent);
        // Setup shared libraries before creating the path list. ART relies on the class loader
        // hierarchy being finalized before loading dex files.
        this.sharedLibraryLoaders = sharedLibraryLoaders == null
                ? null
                : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
    
        reportClassLoaderChain();
    }
    

    我们继续 调用这个DexPathList findClass 看下代码实现

      public Class<?> findClass(String name, List<Throwable> suppressed) {
       for (Element element : dexElements) {
           Class<?> clazz = element.findClass(name, definingContext, suppressed);
           if (clazz != null) {
               return clazz;
           }
       }
    
       if (dexElementsSuppressedExceptions != null) {
           suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
       }
       return null;
    }
    

    他会遍历这个dexElements那这又是个什么东西,他是从创建DexPathList对象时初始化的

    DexPathList(ClassLoader definingContext, String dexPath,
      //做一些判断
           String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
       if (definingContext == null) {
           throw new NullPointerException("definingContext == null");
       }
    
       if (dexPath == null) {
           throw new NullPointerException("dexPath == null");
       }
    
       if (optimizedDirectory != null) {
           if (!optimizedDirectory.exists())  {
               throw new IllegalArgumentException(
                       "optimizedDirectory doesn't exist: "
                       + optimizedDirectory);
           }
    
           if (!(optimizedDirectory.canRead()
                           && optimizedDirectory.canWrite())) {
               throw new IllegalArgumentException(
                       "optimizedDirectory not readable/writable: "
                       + optimizedDirectory);
           }
       }
    
       this.definingContext = definingContext;
    
       ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
       // save dexPath for BaseDexClassLoader
       //因为可以传多个dexPath他们是用':'分割的所以要进行这个路径分割通过makeDexElements方法获取dexElement数组
       this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                          suppressedExceptions, definingContext, isTrusted);
                                          //底下省略
    

    这个dexElements就是通过makeDexElements获取的,到这里我们就知道大概了通过遍历dexElements获取element也就是dex,通过DexPathList.findClass就走完加载流程了

    public Class<?> findClass(String name, ClassLoader definingContext,
               List<Throwable> suppressed) {
           return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                   : null;
       }
    
  • 参考资料
    • https://juejin.cn/post/6844903929562529800
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值