欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java类加载的过程

程序员文章站 2024-01-30 18:09:22
...

一、类加载器

       类加载器是JVM执行类加载机制的前提,其主要任务为根据一个类的全限定名来读取 此类的二进制字节流到JVM内部,然后转换为一个与目标类对应的java.lang.Class对象实例;

        类加载器:ClassLoader

        ①ClassLoader除了将Class加载到JVM之外,还有一个重要的作用就是审查每个类应该有谁加载,它是一种父优先的等级加载机制;

        ②ClassLoader还有一个重要的作用就是将Class字节码重新解析成JVM统一要求的对象格式;

        ClassLoader主要包括defineClass(),findClass(),loadClass(),resolveClass()几个方法:

        defineClass方法 的主要作用是将byte 字节流解析成JVM能够识别的class对象,这个方法意味着 我们不仅仅可以通过class文件去实例化对象,还可以其他方式实例化对象,例如我们通过网络接收到一个类的字节码;defineClass的代码如下:

 protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }


    protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        int len = b.remaining();

        // Use byte[] if not a direct ByteBufer:
        if (!b.isDirect()) {
            if (b.hasArray()) {
                return defineClass(name, b.array(),
                                   b.position() + b.arrayOffset(), len,
                                   protectionDomain);
            } else {
                // no array, or read-only array
                byte[] tb = new byte[len];
                b.get(tb);  // get bytes out of byte buffer.
                return defineClass(name, tb, 0, len, protectionDomain);
            }
        }

        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }

        defineClass()通常是和findClass()一起使用,我们可以通过覆盖父类中的findClass 方法来实现类的加载规则,从而取得加载类的字节码,然后调用defineClass方法生成的Class对象;

 

二、如何加载Class文件

        ClassLoader加载一个class文件到 JVM中,需要经过以下的步骤:

        .class文件----->findClass------>验证、准备、解析----->初始化------>卸载

        即:加载---->连接----->初始化---->卸载

       ①找到.class文件并把这个文件包含的字节码加载到内存中;

       ②字节码验证、Class类数据结构分析及相应的内存分配和最后符号表的链接;

       ③类中静态属性和初始化赋值,以及静态块的执行;

        加载字节码到内存:

        抽象类ClassLoader通过findClass找到指定类并把他的 字节码加载到内存需要的子类中去实现;

        验证:

        验证阶段JVM所执行的一系列验证大概分为:格式验证、语义验证、操作验证、符号引用验证;

        格式验证的主要任务就是检查当前正在加载的字节码文件是否符合JVM规范 ,是否是一个有效的字节码文件,格式验证的主要任务是检查当前正在加载的字节码文件中的前四个字节是否是0xCAFEBABE;

        语义验证:验证字节码信息是否符合java语法规范;

        操作验证:JVM会对类型的方法执行验证,以确保一个类的方法在执行时,不会对JVM产生不良影响不会因此导致JVM的进程出现崩溃;

        符号引用验证:对常量池中的各种符号引用执行验证;

        准备阶段:

        对存放在方法区 中类数据信息的类变量执行初始化,这里所执行 的初始化操作并非是指类加载阶段中的初始化操作,这里仅仅是为类中的所有静态变量分配内存空间,并为其设置一个初始值,而非用户手动执行赋值操作;

        解析阶段:

        主要任务是将字节码常量池中的符号引用全部转换为直接引用,包括类、接口、方法和字段的符号引用。

        初始化:

        在 这个阶段中,JVM会将一个类中所有被static关键字标示的代码统统执行一遍,如果执行的是静态常量,那么用户将会使用用户指定的值覆盖掉之前在准备阶段中JVM为其设置的初始值,如果程序中并没有为静态变量显式指定赋值操作,那么 所持有的值仍然是之前的初始值;反之如果执行的是static代码块,那么在初始化阶段中,JVM就将会执行static代码块中定义的所有操作;

相关标签: JVM