yihau

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

虚拟机把类从Class文件(可能是网络上的二进制流)加载到内存中,并对数据进行校验,解析初始化,然后形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

1、类的生命周期

2、什么情况下开始类的加载?

虚拟机没有规定类加载的时机,但是规定了类初始化的时机,而在类初始化之前,必须要完成之间的过程,类初始化的时机“有且仅有”以下几个时间:

  • 遇到new、getstatic、putstatic或invokestatic这四条字节码指令,就是说触发构造器或者类的成员的时候(但是在使用一个被final修饰static字段不会触发,因为这样的字段在编译期就被放在调用这个字段的类的常量池中去了,即定义的类和调用的类已经没有关系了,所以不会触发)。
  • 使用java.lang.reflect包的方法对类进行反射调用。
  • 初始化类的时候要首先初始化其父类,因为初始化类的时候会首先调用父类的构造器
  • 虚拟机启动时候,要执行一个类的main方法,则先初始化这个类,main方法是static的。
  • 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结构REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄。

3、类加载的过程

3.1、加载

  加载阶段完成三件事,1,通过类的全限定名来获取这个类的二进制字节流;2、将字节流转换成方法区的运行时数据结构;3、生成一个代表这个类的java.lang.Class对象。

3.2、验证

  验证阶段正如其名,验证过去的字节流是否符合Class文件的规范,版本是否匹配,语法是否规范等。

3.3、准备

  准备阶段会把static修饰的类变量分配内存并且赋值为数据类型的默认值,但是static、final对象会赋值为实际值。

3.4、解析

  解析阶段虚拟机会将常量池中的符号引用替换为直接引用,符号引用类似于某个对象的名字,直接引用类似于对象的地址。

3.5、初始化

  这里的初始化还是说类的初始化所以和类的构造器没有什么关系,针对的仅仅是类中的静态static成员。虚拟机在编译的时候会为我们的类生成一个<clinit>()方法(当然,如果类中没有静态变量或者静态语句块的话是不需要生成的)。初始化阶段就是执行这个clinit方法的时间,会为静态变量赋值,执行静态语句块。

4、类加载器

  实现把类加载到虚拟机中这个动作的代码块称为“类加载器”,所以类加载器和虚拟机是在一定程度上是可以分离的。

4.1、两个类“相等”吗?

  对于全限定名相同的类就一定相等吗?,比较两个类是否相等,只有在这两个类是同一个类加载器加载的前提下才有意义。这里说的相等是指类的Class对象的equals()方法、isAssignableFrom()方法、和isInstance()方法的返回结果,也包括使用instanceof关键字做对象所属关系判定等结果,也不能通过强制类型转换来转换这两个类的实例对象。

4.2、双亲委派模型

  从JVM的角度讲,只有两种类加载器,一种是启动类加载器,这个类加载器是由C++实现的属于JVM自身的一部分,另一种是其他类加载器,由Java实现,独立于虚拟机之外。

  从开发人员来看,类加载器划分为三类,

  • 启动类加载器,负责加载<JAVA_HOME>\lib目录中的类以及,命令参数指定为-XbootClasspath的路径中的类
  • 扩展类加载器,负责加载<JAVA_HOME>\lib\ext目录中的类
  • 应用程序类加载器,这个类加载时是ClassLoader中getSystemClassLoaser()方法的返回值,是系统的默认类加载器,负责加载用户的类。

 类加载之间有一种层次关系,我们称之为双亲委派模型;

双亲委派模型要求出了启动类加载器之外,其余的类加载器都要有自己的父类加载器。不过父子关系不是由继承实现的,而是有组合实现的,就是在子类中保存有父类的引用。双亲委派模型的工作过程是:如果一个类加载器收到类的加载请求,会首先请求父类去加载这个类,如果父类加载不了(对应的类不在父类加载器的类路径中),子类加载器才会自行加载。这样做的好处是可以保证类和类加载器具备层次结构,例如Object类,处于rt.jar中,这个类只会被启动类加载器加载,这样因此Object类在程序的各种类加载器环境中都是一个类。

 Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        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.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                }
            }

  在自定义类加载器的时候应该继承ClassLoader类,实现其中的findClass方法,在loadClass(ClassLoader类中已经实现)中,已经定义了双亲委派的逻辑,在父类加载不能加载的时候会调用findClass方法,所以我们只需要自己实现findClass方法即可。

/**
     * Finds the class with the specified <a href="#name">binary name</a>.
     * This method should be overridden by class loader implementations that
     * follow the delegation model for loading classes, and will be invoked by
     * the {@link #loadClass <tt>loadClass</tt>} method after checking the
     * parent class loader for the requested class.  The default implementation
     * throws a <tt>ClassNotFoundException</tt>.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

 

线程上下文类加载器,某个抽象类定义在rt.jar包中,所以他是由启动类加载器来加载的,但是这个抽象类只是提供了SPI的接口,具体实现是由厂家自己实现的,但是厂家自己实现的类肯定不在rt.jar中,这样的抽象类如果要调用实现类就出现了父类加载器找不到实现类,只能靠子类加载器去加载,这是违背双亲委派模型的。所以就有了线程上下文类加载器,线程上下文类加载器可以在线程中设置Thread,Thread中有一个setContextClassLoader()方法,如果不设置的话默认为应用程序类加载器。然后父类可以通过这个类加载器来加载自己无法加载的类。

posted on 2018-03-10 12:17  yihau  阅读(140)  评论(0编辑  收藏  举报