类加载机制

java中的类,从编写,到运行时候是一个过程,那么这样的代码,jvm怎么能保证去运行起来呢,这就涉及到类加载机制

一个类的声明周期: 由加载  ->验证  准备,解析,初始化,使用,卸载,那么我们应该重点关注前面五个过程

1 加载:

  类是由什么来加载的呢,在学习过程中我们应该多问几个为什么,这样才可以来提高我们的技术要点,所谓的加载就是加载class文件,也就是加载类的内容。从而又引发另外得思考,怎么加载呢,加载过程中应该注意什么问题呢,是不是所有人写的类都可以进行加载呢

  在java中类是由类加载器进行加载的,类加载器分为三种:

  核心类加载器,也叫bootsrap类加载器,这个加载器主要作用是加载jdk核心类库下的类。rt.jar等待

  类扩展库加载器: 加载jre_home/jre/lib/ext目录下jdk扩展包,或者用户自己的目录

  用户程序应用程序加载器: 加载java。class.path指定目录下用户应用程池

  那么这三个类加载器,谁大谁小呢,很简单核心类加载器最大,类扩展第二,应用程序第三,,java累加载机制采用了双亲委派机制

如果子类不能加载就交给父类去加载,提供了沙箱环境

  

public class ClassLoaderView {
    public static void main(String[] args) throws Exception {
        // 加载核心类库的 BootStrap ClassLoader
        System.out.println("核心类库加载器:"
                + ClassLoaderView.class.getClassLoader().loadClass("java.lang.String").getClassLoader());
        // 加载拓展库的 Extension ClassLoader
        System.out.println("拓展类库加载器:" + ClassLoaderView.class.getClassLoader()
                .loadClass("com.sun.nio.zipfs.ZipCoder").getClassLoader());
        // 加载应用程序的
        System.out.println("应用程序库加载器:" + ClassLoaderView.class.getClassLoader());

        // 双亲委派模型 Parents Delegation Model
        System.out.println("应用程序库加载器的父类:" + ClassLoaderView.class.getClassLoader().getParent());
        System.out.println(
                "应用程序库加载器的父类的父类:" + ClassLoaderView.class.getClassLoader().getParent().getParent());
    }
}

 

2 验证

  验证class文件是否符合规范,语义分析,引用验证,字节码验证

  对于这个部分我们应该怎么去理解呢,

  首先对于一个东西需要适配,那么需要注意什么呢,所以java'在这个过程中进行验证,主要验证是否符合规范,例如版本,是否是java文件,引用验证(此时如果验证不通过会报classnotfound异常),

为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

  文件格式的验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理,该验证的主要目的是保证输入的字节流能正确地解析并存储于方法区之内。经过该阶段的验证后,字节流才会进入内存的方法区中进行存储,后面的三个验证都是基于方法区的存储结构进行的

  元数据验证:对类的元数据信息进行语义校验(其实就是对类中的各数据类型进行语法校验),保证不存在不符合Java语法规范的元数据信息

  字节码验证:该阶段验证的主要工作是进行数据流和控制流分析,对类的方法体进行校验分析,以保证被校验的类的方法在运行时不会做出危害虚拟机安全的行为

  符号引用验证:这是最后一个阶段的验证,它发生在虚拟机将符号引用转化为直接引用的时候(解析阶段中发生该转化,后面会有讲解),主要是对类自身以外的信息(常量池中的各种符号引用)进行匹配性的校验

3 准备

  分配内容,设置类static修饰的变量初始化值,正式为类变量分配内存并设置类变量初始值(通常情况下是数据类型的零值)的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化的时候随着对象一起分配在Java堆中。

4 解析

  解析类中的类,接口,字段,方法,虚拟机将常量池内的符号引用替换为直接引用的过程。

  类或接口的解析:判断所要转化成的直接引用是对数组类型,还是普通的对象类型的引用,从而进行不同的解析。

  字段解析:对字段进行解析时,会先在本类中查找是否包含有简单名称和字段描述符都与目标相匹配的字段,如果有,则查找结束;如果没有,则会按照继承关系从上往下递归搜索该类所实现的各个接口和它们的父接口,还没有,则按照继承关系从上往下递归搜索其父类,直至查找结束

  类方法解析:对类方法的解析与对字段解析的搜索步骤差不多,只是多了判断该方法所处的是类还是接口的步骤,而且对类方法的匹配搜索,是先搜索父类,再搜索接口。

  接口方法解析:与类方法解析步骤类似,知识接口不会有父类,因此,只递归向上搜索父接口就行了。

5 初始化

  为静态变量赋值,执行静态代码块,这是类加载的最后一步,真正执行类中定义的字节码,也就是.class文件。 初始化阶段是执行类构造器方法的过程,以及真正初始化类变量和其他资源的过程

  初始化是类加载过程的最后一步,到了此阶段,才真正开始执行类中定义的Java程序代码。在准备阶段,类变量已经被赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员通过程序指定的主观计划去初始化类变量和其他资源,或者可以从另一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过程。

6 使用

  创建实例对象

7 卸载

  从方法去冲卸载,gc机制,当然我们也可以进行手动卸载system.gc

posted @ 2020-03-02 20:27  菩提树下的韦小宝  阅读(134)  评论(0编辑  收藏  举报