为了账号安全,请及时绑定邮箱和手机立即绑定

畅谈类加载的过程

标签:
Java

什么是类加载

  前面我们说道java外交部长(编译器)就是做到了把java国王交代的任务分给自己的小弟(JVM),那么他交代给小弟JVM的过程其实上就是我的类加载过程;既然有这个交代的过程那肯定就有一个传话的人(公公),要不然JVM怎么知道老大的交代的任务呢,我们一般把这个人叫做类加载器,他负责把外交部长的编译产物(字节码文件)送到JVM的家中。

类加载器

官方解释:

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

英文名字:ClassLoader

1、ClassLoader除了将Class加载到JVM之外,还有一个重要的作用就是审查每个类应该有谁加载,它是一种父优先的等级加载机制;
2、ClassLoader还有一个重要的作用就是将Class字节码重新解析成JVM统一要求的对象格式;

主要方法

1、defineClass();
  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;
    }

2、findClass();
  实现类的加载规则,取得要加载类的字节码,通常是和defineClass()一起使用的;查找类,返回java.lang.Class类的实例;
3、loadClass();
  加载类,返回java.lang.Class类的实例;
4、resolveClass();
  连接指定的一个类,如果你想在类被加载到JVM中的时候就被链接(Link),则调用resolveClass()方法;

Class文件的加载过程

  我们都知道类的加载过程有,加载、链接、初始化、卸载;而链接过程又分为验证、准备、解析阶段;那么我们整天挂在嘴边的东西,实质上又是调用了什么方法了呢,下面先看一个图:


https://img1.sycdn.imooc.com//5d53b9b700016be808970351.jpg

01.png

1、找到.class文件并把这个文件包含的字节码加载到内存中;
2、字节码验证、Class类数据结构分析及相应的内存分配和最后符号表的链接;
3、类中静态属性和初始化赋值,以及静态块的执行;

步骤分析

加载

  调用findClass()方法找到对应的字节码文件;

验证:

  验证阶段JVM所执行的一系列验证大概分为:格式验证、语义验证、操作验证、符号引用验证;
1、格式验证的主要任务就是检查当前正在加载的字节码文件是否符合JVM规范 ,是否是一个有效的字节码文件,格式验证的主要任务是检查当前正在加载的字节码文件中的前四个字节是否是0xCAFEBABE;
2、语义验证:验证字节码信息是否符合java语法规范;
3、操作验证:JVM会对类型的方法执行验证,以确保一个类的方法在执行时,不会对JVM产生不良影响不会因此导致JVM的进程出现崩溃;
4、符号引用验证:对常量池中的各种符号引用执行验证;

准备阶段:

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

解析阶段:

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

初始化:

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



作者:Taoyongpan
链接:https://www.jianshu.com/p/47ef91131510


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消