1.定义:
类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机
只接受用的Java类型。
2.类加载的生命周期:
2.1加载:
在加载阶段,虚拟机需要完成的3件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流(可以从Class文件获取也可以不)。
- 将这个字节流所代表的静态存储结构转化成方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
需要注意的是,对于非数组类的加载是由类加载器来完成的,也可以自定义的类加载器去完成。我们接下来会将类加载器。
而对于数组类而言。并不通过类加载器创建,而是由java虚拟机创建,但是数组的元素类型还是由类加载器去创建。
2.2验证:
验证是连接的第一步,目的是为了确保Class文件的字节流中包含的信息符号符合虚拟机的要求,防止有害的字节流。大致有四个方面需要验证。
- 文件格式验证(包括Class文件内容的判断,是否满足Class文件的规范。比如是否是以魔数开头,常量类型是否存在,)。
- 元数据验证(包括类的描述信息是否符合java语言规范,是否有父类,父类是否继承不允许继承的类,是否实现接口所有的方法等等)
- 字节码验证(这是最复杂的一个阶段,目的是为了保证被校验的方法在运行时不会危害虚拟机)
- 符号引用验证(发生在虚拟机将符号引用转化成直接引用的时候,对常量池中的各种符号引用信息进行匹配验证)
如果我们不需要验证也可以通过设计虚拟机参数关闭验证,除了文件验证是验证字节流之外,其他验证都是基于方法区的存储结构,因为第一个验证结束之后字节流就进入内存进行存储。
2.3准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。分配只包括静态变量(static),不包括实例变量。
2.4.解析
解析阶段是虚拟机将常量池内的符号引用替换成直接引用的过程。我们先解释一下符号引用和直接引用。
符号引用:一组符号来描述所引用的目标,可以是Class文件里的字面量。
直接引用:直接指向内存里目标的指针,相对偏移量或者一个能间接定位到目标的句柄。
解析的动作主要针对类或接口,字段,类方法,接口方法,方法类型,方法句柄和调用点限定符第7类符号引用。解析可以在加载时就对常量池中的符号引用进行解析,还是等到一个符号引用将要被使用前才去解析它。而且一个符号引用进行多次解析请求是很常见的,但是虚拟机可以保证多次解析的结果都是同一个实体。虚拟机可以实现对第一次解析结果的缓存从而避免解析动作重复进行。
2.5初始化
初始化阶段才是真正开始执行字节码,准备阶段变量已经赋过一个默认值,而初始化阶段,则根据程序定义的去初始化类变量和其他资源。初始化就是执行类构造器<client>方法的过程。 <client>方法是由编译器自动收集类中的所有的类变量赋值动作和静态语句块中的语句合并产生,并且父类的<client>先执行。该操作也是线程安全。
2.6类初始化
五种初始化的情况:
- 遇到 new一个对象,读写静态字段或者调用静态方法(final除外,因为编译阶段通过常量传播优化,所谓的常量传播优化指的就是在编译优化时将已经计算结果的变量替换成常量,已经存到相应类的常量池中)
- 对类反射调用时,需要先初始化。
- 当初始化一个类时,先初始化父类。
- 虚拟机先初始化包含main()方法这个类。
- 对MethodHandle实例的解析结果句柄判断是否要初始化。
3种不初始化:
- 子类引用父类的静态字段不会引起子类的初始化。
- 作为数组类型的类,不会引起类的初始化。
- final修饰。
3.双亲委派模型
java虚拟机只存在两种类加载器;一种是启动类加载器(c++编写,无法被java程序直接调用)。属于虚拟机的一部分;另一种是所有其它的类加载器,独立于虚拟机,并且全部继承于抽象类java.lang.ClassLoader。其它的虚拟机又可以细分为:扩展类加载器,应用程序类加载器,自定义类加载器。只需要实现loadClass()方法。
注意:类加载器一般不用继承来实现父子类,而是通过组合,应用程序类加载器是一个默认的类加载器。
双亲委派模型的工作过程:如果一个类加载器收到加载请求,他并不会自己去加载这个类,而是请求委派给父类加载器去完成,因此所有的加载请求是应该传送到启动类加载器,这样类在程序各个类加载器环境中都是同一个类。