先用一张图来看看一个类的生命周期
可以看到一个类的生命周期需要经历几个过程:加载、连接、初始化。其中连接分为三个步骤:验证、准备、解析。依次说明
加载
在加载过程,虚拟机主要完成三件事
1、通过一个类的全限定名来获取定义此类的二进制字节流
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3、在java内存堆中生成一个代表这个类的java.lang.Class对象,作为这个类的各种数据的访问入口。
第一步主要是获取一个类的二进制字节流,意思就是把类以流的形式加载进内存,类的来源没有说,可以是jar包,也可以是class文件或者是apk文件。这个特性是能够实现插件化技术的理论基础。
第二步就是在获取到这个字节流以后,虚拟机就会把类中的静态存储结果保存到方法区中,保存的过程会转化对应方法区中的数据结构,所以说静态的结构都保存在内存中的方法区中。
第三步是当类加载进内存以后,每个类都会生成一个对应的Class对象,当我们使用这个类的时候,都是通过此Class对象为入口来使用的,比如我们写程序的时候通过 new 关键字创建一个类的对象的时候,也是通过这个类的Class对象来创建的。
连接
连接阶段主要分验证、准备和解析。
验证:主要是对类中的语法结构是否合法进行验证,确认类型符合Java语言的语义。
准备:这个阶段是给类中的类变量分配内存,设置默认初始值,比如一个静态的int变量初始值是0,布尔变量初始值是false。
解析:在类型的常量池中寻找类,接口,字段和方法的符号引用,把这些符号引用替换成直接引用的过程。
解析的过程可能不好理解,关于符号引用和直接引用是什么意思可以暂时忽略,这个过程可以理解为一开始虚拟机对加载到内存中的各种类、字段等并没有一一编号,只是通过一个符号去表示,在解析阶段,虚拟机把内存中的类、方法等进行统一管理起来。
初始化
初始化阶段才真正到了类中定义的java代码的阶段,在这个阶段会对类中的变量和一些代码块进行初始化,比如对类变量进行初始化,在准备阶段对类变量进行的默认初始化,到这个阶段就对变量进行显式的赋值,其中静态代码块就是在这个阶段来执行的。
初始化不会马上执行,当一个类被主动使用的时候才会去初始化,主要有下面这几种情况:
1、当创建某个类的新实例时(如通过new或者反射等)
2、当调用某个类的静态方法时
3、当使用某个类或接口的静态字段时
4、当初始化某个子类时
Android类加载机制
Java中的ClassLoader是加载class文件,而Android中的虚拟机无论是dvm还是art都只能识别dex文件。因此Java中的ClassLoader在Android中不适用。Android中的java.lang.ClassLoader
这个类也不同于Java中的java.lang.ClassLoader
。
Dalvik虚拟机识别的是dex文件,而不是class文件,因此,我们加载的是dex文件,或者包含dex文件的apk文件或jar文件。
先来看下Android类加载器的结构图
可以看出,PathClassLoader和DexClassLoader都是继承BaseDexClassLoader,那这两种加载器有什么区别呢?答案是:没有任何区别,严格的说,是在API26(含)以后,这两种类加载器就没有任何区别了,而在API26以前,还是有一定的区别的,主要区别就是跟网上说的,PathClassLoader主要是加载系统已经安装的apk,而DexClassLoader主要是加载未安装的apk/jar/zip/dex。我们来看下源码就知道了
先看下PathClassLoader的源码
package dalvik.system;
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
}
再来看下DexClassLoader源码
package dalvik.system;
import java.io.File;
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
从源码中很明显的看出,两者的唯一区别就是在于optimizedDirectory这个参数的不同,所以接下来看下BaseDexClassLoader的源码
先来看看API26以前的BaseDexClassLoader构造函数源码
再来看API26(含26)以后的BaseDexClassLoader构造函数源码