美文网首页
Java的类加载机制

Java的类加载机制

作者: MacroZH | 来源:发表于2018-04-26 22:41 被阅读0次

    类从加载到虚拟机内存到卸载出内存共经历7个阶段:加载,验证,准备,解析,初始化,使用,卸载。其中验证、准备、解析被称为连接阶段。如图


    类加载.jpg

    1加载

    加载时间由虚拟机来把握, 加载阶段完成3件事情
    1.通过类的全限定名获取二进制字节流。
    二进制字节流可以来自于类文件,也可能来自于其他地方如ZIP包、网络、运行时计算生成等等。
    2.将二进制字节流转化成为方法区的运行时数据结构
    3.在内存中生成Class对象,这个对象存放在方法区,可以用来反射。

    2验证

    验证阶段主要是为了确保Class文件字节流符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
    包括文件格式验证、元数据验证、字节码验证、符号引用验证。

    3准备

    准备阶段为变量分配内存并设置变量初始值,这个初始值并不是代码中定义时候赋予的初值,通常情况下初始值设置为零值,进行分配的变量仅包括类变量(static修饰的变量)。代码中赋予的值会在初始化时执行类构造器<clinit>()方法时候赋予。
    定义的常量(static final修饰)会直接赋予代码中设置的值。

    4解析

    解析阶段虚拟机将符号引用替换为直接引用。

    符号引用:使用一组符号来描述所引用的目标,可以是任何形式的字面量,只要使用时无歧义定位到目标即可。例如"org/fenixsoft/clazz/TestClass"

    直接引用:直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。

    5初始化

    类初始化是类加载过程的最后一步,初始化阶段开始执行类中定义的Java程序代码。虚拟机严格规定了5种情况会立即对类进行初始化:
    1.遇到new、getstatic、putstatic、invokestatic字节码指令
    2.使用反射访问类
    3.当初始化一个类时,会先出发父类初始化(如果父类还未初始化)
    4.虚拟机启动时运行的主类(如包含main()方法的那个类)
    5.使用java.lang.invoke.MethodHandle解析REF_getStatic、REF_putStatic、REF_invokestatic的方法句柄,这个方法句柄对应的类没有进行初始化,则需要先出发其初始化(暂时理解为使用MethodHandle进行反射调用方法时,需JDK7支持)。

    这5种情况会触发初始化,被称为主动引用,其他引用类的方式不会触发初始化,被称为被动引用(如子类引用父类的静态字段,或数据定义应用类 MyClass[] arr=new MyClass[10] ,或 引用类中定义的常量字段)。

    6类加载器与双亲委派模型

    类加载器用于实现类的加载动作,对于任意一个类,都需要由加载它的类加载器和这个类本身共同确定这个类在虚拟机中的唯一性,因此,使用两种类加载器去加载同一个Class文件会得到两个类。也就是说对这两个类对象使用equals()方法,instanceof关键字都会返回false。
    例如

    public class TestClassLoader {
        public static  void  main(String[] args)
        {
            ClassLoader myLoader=new ClassLoader() {
                @Override
                public Class<?> loadClass(String s) throws ClassNotFoundException {
                    try {
                      String className=s.substring(s.lastIndexOf(".")+1)+".class";
                        InputStream is=getClass().getResourceAsStream(className);
                        if(is==null)
                        {
                            return super.loadClass(s);
                        }
                        byte[] b=new byte[is.available()];
                        is.read(b);
                        return  defineClass(s,b,0,b.length);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return  null;
    
                }
    
            };
    
            try {
                Object object=myLoader.loadClass(TestClassLoader.class.getName()).newInstance();
                System.out.println(object.getClass());
                System.out.println(object instanceof TestClassLoader);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    }
    
    class com.example.java.TestClassLoader
    false
    

    系统提供3种类加载器:
    1.启动类加载器
    2.扩展类加载器
    3.应用程序类加载器
    类加载器之间的层次关系称为类加载器的双亲委派模型。


    双亲委派.png

    双亲委派模型的工作过程是:如果一个类加载器收到类加载请求,他会把这个请求为派给父类加载器,每一个层器的加载器都是这样,因此所有的请求都会传递到最顶层的类加载器,当最顶层的累加器不能加载时候会向下交给子类加载。因此像java.lang.Object这样的基础类一定会由同一个类加载加载,这也就保证了内存中只会存在一个Object类对象。

    相关文章

      网友评论

          本文标题:Java的类加载机制

          本文链接:https://www.haomeiwen.com/subject/sxzmlftx.html