类加载

2019-02-17  本文已影响0人  麦香小瑜儿

jvm的类加载,可以分为以下3个步骤

加载

加载,是指jvm借助类加载器查找字节流,并且据此创建类的过程。对于数组类来说,它并没有对应的字节流,而是由 Java 虚拟机直接生成的。
系统提供的类加载器有

双亲委派模型

双亲委派

过程:如果一个类加载器收到加载类的请求,它首先不会尝试自己去加载这个类,而是把这个请求委派给父加载器去完成。每一个层次的类加载都是如此,因此所有的加载请求最终都应该传递到顶层的启动类加载器中。只有当父加载器反馈无法完成加载请求时(搜索不到这个类),子加载器才会尝试自己去加载。
双亲委派的实现逻辑在java.lang.Classloader(jdk1.8.0)的loadClass方法之中:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            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);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

总结

自定义类加载器

链接

链接可分为验证准备解析3个阶段。

  1. 验证阶段的目的,在于确保被加载类能够满足 Java 虚拟机的约束条件。包括:
  1. 准备阶段为类变量分配内存并设置类变量初始值(注意不是实例变量,这里的初始值是指类型的默认值如int的默认值为0)
  2. 解析阶段是虚拟机将常量池内的[符号引用]替换(#jump1)为直接引用的过程。

初始化

初始化阶段是执行类构造器<clinit>()(不是实例构造器)的过程。<clinit>()是编译器自动收集类中所有的赋值语句和静态语句块(static{}块)合并产生的(按照在java文件中出现的顺序)。

初始化时机

以下几种情况,如果类没有初始化,则需要触发其初始化。

总结

一些定义

符合引用: 以一组符合来描述所引用的目标。可以是任何形式的字面量,与jvm的内存布局无关,引用的目标不一定被加载到内存中。
直接引用: 可以是直接指向目标的指针、相对偏移量或者一个能够间接定位到目标的句柄。直接引用的目标一定存在jvm内存中。

实例的初始化

(需要继续完善补充)
在Java程序中类可以被明确或者隐含地实例化,有四种途径:明确使用new操作符;调用Class或者Constructor对象的newInstance()方法;调用任何现有对象的clone()方法;或者通过objectInputStream类的getObject()方法反序列化。
当虚拟机创建一个新的实例时,都需要在堆中为保存对象的实例分配内存。所有在对象的类中和它的超类中声明的变量(包括隐藏的实例变量)都要分配内存。一旦虚拟机为新的对象准备好堆内存,它立即把实例变量初始化为默认的初始值。这一点很类似于类变量在链接的准备阶段赋予默认初始值是一样样的。
一旦虚拟机完成了为新的对象分配内存和为实例变量初始化为默赋予正确认的初始值后,接下来就会为实例变量的初始值。即调用对象的实例初始化方法,在java的class文件中称之为< init >()方法,类似于类初始化的< clinit >()方法。
一个< init >()方法可能包含三种代码:

调用另一个< init >()方法
实现对任何实例变量的初始化
构造方法体的代码

参考

上一篇下一篇

猜你喜欢

热点阅读