美文网首页
2019-05-23 JVM类加载机制

2019-05-23 JVM类加载机制

作者: 惜小八 | 来源:发表于2019-05-23 16:00 被阅读0次

    原文:https://www.cnblogs.com/dooor/p/5289994.html
    参考自:https://blog.csdn.net/WantFlyDaCheng/article/details/81808064

    1.JVM类加载过程

    类加载到类被卸载过程包括7个阶段:

    1.加载  通过类的全限定名把类文件的二进制流加入进来,通过这个字节流(这个二进制流也是我们代理类的方法),然后通过这个二进制流把静态存储结构转化为运行时方法区的结构(不包括类变量,类变量在准备阶段),在内 存中生成一个Class对象,作为方法区访问的入口。
    
    2.验证  验证是验证Class文件的字节流包含的信息是否符合当前虚拟机的要求规范,防止恶意攻击、
    
    3.准备  在方法区为类变量分配内存和设置初始值,这个时候的初始值是数据的0值,不是我们定义的值,如果是常量的话准备阶段就会设置为我们定义的值
    
    4.解析  将符号引用(这里的符号引用指的是字面量的形式,只需要无歧义地定位到目标)替换为直接变量
    
    5.初始化  类初始化 阶段是我们加载过程的最后一步,执行类构造器,合并static语句,有static的顺序决定。
    
    6.使用
    
    7.卸载
    

    (一)类加载机制

    JVM把class文件加载到内存,并对数据进行校验、准备,解析和初始化,最终形成JVM可以直接使用的java类型的全过程。


    image.png
    (1)加载

    将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与

    class文件被加载之后,方法去会被分出一块内存,存储这个.class文件的所有信息,然而这个.class文件并不能被我们所直接利用
    ,我们还需要一个可以直接使用的对象,此时堆内存就会发挥作用了,当类的信息被加载在方法区之后
    ,JVM就会在堆当中创建一个java.lang.Class对象,这个对象就可以作为方法区数据的访问入口,即通过反射去获取类的信息
    ,实际上,我们每一次new一个对象的时候,底层就是java.lang.Class对象去完成的,一个类在JVM当中只能有一个java.lang.Class对象。
    

    总结如下:
    1.类的加载的最终产品是位于堆区中的Class 对象
    2.Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口


    image.png
    (二)连接
    (1)验证:确保加载的类信息符合JVM规范,没有安全方面的问题,一般情况由javac编译的class文件是不会有问题的,但是可能有人的class文件是自己通过其他方式编译出来的,这就很有可能不符合jvm的编 译规则,这一步就是要过滤掉这部分不合法文件
    (2)准备: 正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法去中进行分配,设置的初始值为JVM默认的初始值,并非程序编写者所赋予的真正值,这里注意,只是为静态变量分配内存,此时是没有对象实例的,即成员变量此时不会被分配内存或赋初值
    (3)解析:把类中的符号引用转化为直接引用(指针)
    (三)初始化:

    为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋予的才是程序编写者为变量分配的真正的初始值

    (四)使用
    (五)卸载

    (二)类什么时候会被加载?

    Java程序对类的使用方式可分为两种
    – 主动使用
    – 被动使用

    当类被引用的加载,类只会加载一次
    
    类的主动引用(一定会发生类的初始化)
    new一个类的对象
    调用类的静态成员(除了final常量)和静态方法
    使用java.lang.reflect包的方法对类进行反射调用
    当虚拟机启动,java Demo01,则一定会初始化Demo01类,说白了就是先启动main方法所在的类
    当初始化一个类,如果其父类没有被初始化,则先初始化它父类
    
    
    类的被动引用(不会发生类的初始化)
    当访问一个静态域时,只有真正声名这个域的类才会被初始化
    通过子类引用父类的静态变量,不会导致子类初始化
    通过数组定义类的引用,不会触发此类初始化
    引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)
    
    class Test2{
      public static final int n = 2;
    
      static{
        System.out.println("test");
      }
    }
    
    public class Test1 {
      public static void main(String[] args) {
        System.out.println(Test2.n);
      }
    
    }
    
    正确答案是2 。 Test2中的静态代码块是没有运行的,因为Test2并没有初始化
    注意,这里的final关键字不可缺少,我们知道变量被关键字fianl修饰之后就不可修改,亦即此变量相当于编译期常量(是相当于并非就是常量),常量在java编译期已经确定,不需要初始化,
    但是把fianal去掉,或者把 final int n = 2 改为 final int n = new Random()。,运行的结果将变为 test  ,2 ,因为n的值为变量或者n值在编译期不能确定,就必须经过初始化(运行期)才能使用n的值。
    
    image.png

    相关文章

      网友评论

          本文标题:2019-05-23 JVM类加载机制

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