java 类加载机制与双亲委派模型

一、类的加载过程

        类的加载过程主要分为三个步骤:1)装载load、2)链接link、3)初始化Initial

        1)装载(load):查找并加载类的二进制数据

        2)链接(link):主要分为三个步骤

            验证:确保被加载的类的二进制数据是合法正确的。由编译器生成的class文件肯定是符合JVM字节码格式的。也有可能黑客自己生成.class文件来作其他用途。

            准备:为类的静态变量分配内存,并将其初始化为默认值

            解析:将类的符号引用转为直接引用

        3)初始化(Initial):为类的静态变量赋予正确值

            准备阶段和初始化阶段的不同点:private int static i=10; 准备阶段是将为i变量分配一个整形的内存,初始化阶段再给其赋值10。

二、类的初始化     

扫描二维码关注公众号,回复: 2274593 查看本文章

     类的初始化的主要流程:

            a.如果这个类还没有被装载、链接,先装载链接

            b.如果这个类存在直接父类,并且这个类还没有被初始化,就初始化直接父类,在一个类加载器中类只能加载一次

            c.加入类中存在初始化语句,如static变量或static代码块,就依次执行初始化语句

      类在什么时候去初始化:

            a)创建一个类的时候,也即new一个对象

            b)java 反射获取一个类的时候Class.forName("....")

            c)访问某个类或接口的静态变量,或者对该静态变量赋值

            d)调用类的静态方法

            e)初始化一个类的子类,会先初始化父类

            f)jvm启动时标明的启动类

三、类的加载

    类的加载是指将类的二进制文件.class文件加载到内存中,放置在运行时的方法区,然后在堆内创建这个类的java.lang.Class对象,用来封装类在方法区内的对象。类加载的最终产品是位于堆内的class对象。class对象封装了在方法区内的数据结构,并且提供了供访问这些数据结构的接口

    加载类的方式:

    1)从本地系统直接加载

    2)通过网络下载.class文件

    3)将java源文件编译为.class文件

    4)从zip/jar等归档文件中加载

四、类加载器

    jvm的类加载器是通过ClassLoader及其子类来完成,jvm提供三个ClassLoader:Bootstrap ClassLoader、Extension ClassLoader、AppClassLoader。另外还可以自定义ClassLoader 如tomcat、jboss等都有自定义ClassLoader

    1)Bootstrap ClassLoader  

    负责加载$JAVA_HOME里面jre/lib/rt.jar的所有class,由c++实现不是ClassLoader的子类

    2)Extension ClassLoader

    负责加载$JAVA_HOME中扩展功能的一些jar

    3)App ClassLoader

    负责加载classpath指向的jar包及目录下的class文件

    4)Custom ClassLoader

     自定义类加载

    由于一个类只能加载一次,类加载器在加载过程会检查类是否被加载,自底向上检查,custom classloader-->app classloader-->extension classloader -->bootstrap classloader.但是类的加载是自顶向下的过程加载,也即由上层逐层加载此类

    总结:

        java在编译时不需类加载器,编译阶段只是将java代码编译成为jvm可识别的字节码class文件,在运行过程中才会调用类加载器。classLoarder,顾名思义即类加载器,负责将class文件加载到内存中。所使用的策略是双亲委派模型。JVM提供三个ClassLoarder,即Bootstrap ClassLoarder、Extension ClassLoarder、App ClassLoarder。各个加载的位置不一样。对于ExtensionClassLoader和AppClassLoarder有一个共同的父类ClassLoader,代码:

public abstract class ClassLoader {
    private final ClassLoader parent;
    protected Class<?> loadClass(String name, boolean resolve);
 
  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 {
                    //当父加载器不存在的时候会尝试使用BootStrapClassLoader作为父类
                    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
                }
                //c为null则证明父加载器没有加载到,进而使用子类本身的加载策略`findClass()`方法
                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;
        }

}

        由上可知,类加载在第一次加载时先查看是否已经加载过此类了,如果没有,尝试用父加载器去加载,如果父加载器没有加载到,则用子类本身的加载策略。

        那么综上所述,什么是双亲模型:

        使用parent加载器加载---》如果parent不存在使用BootstrapClassLoader加载---》如果加载不到则使用子类本身的加载器。那么双亲委派模型就是parent委托对象与BootsrapClassLoader最顶端的加载器。另外需要注意的地方是,BootsrapClassLoader本身不是java对象,是C++实现的JVM加载器。更通俗的来说,classLoader就是parent与BootsrapClassLoader,只是当parent不存在时用BootsrapClassLoader而已。

    为什么要用委派模型:

    回答这个问题需要先回答jvm是如何判断两个类是同一个类,也即类名包括所在的包相等,且是同一个类加载器加载的,那么两个对象才相等。对于Object类加载,由于其都是由父加载器先加载,所以能保证所有的子类都是由同一个ClassLoader加载。也就能保证对象相等。简单来说,类加载器由优先级高的加载器先加载,再由优先级低的加载器加载。

    java如何查看已被类加载器加载的类:

    ClassLoader有一个private字段classes,这是一个vector,包含了这个类加载器已经加载了的类的class对象

    Field f=ClassLoader.class.getDeclaredField("classes");
    f.setAccessible(true);
    f.get(某个类加载器);
    比如
    Vector classes=(Vectoe)f.get(ClassLoader.getSystemClassLoader());
    这样就取得了这个类加载器当前已加载的类。

    如何打破双亲委派机制?    双亲委派模型只是JVM的规范要求,实际上自己的自定义ClassLoader尊不遵循这个规范完全按照自己的业务需求来定。重写LoaderClass方法,将需要特殊对待的类优先处理,非特殊范围内的类调用super()即可。

猜你喜欢

转载自blog.csdn.net/weixin_40018934/article/details/80981581