Java面试准备之JVM详细研究三(类加载机制)

类加载过程

一个类从编写完成后,编译为字节码之后,它要装载进内存有七个阶段:

  加载 => (验证-> 准备-> 解析)=> 初始化=> 使用=> 卸载


括号中的三个步骤可以整合成为 “连接”步骤。其中的步骤并不是一个阶段结束,一个阶段才开始的。只是说他们的开始阶段基本遵循此顺序(解析阶段更是可能在使用的时候才发生,目的是配合动态绑定),这些阶段都是互相交叉的混合式进行的,通常会在一个阶段执行过程中调用或激活另一个阶段。

  1.加载

    ”加载“的过程是”类加载“过程的一个阶段,在家在阶段,虚拟机需要完成以下三件事:

      1)通过一个类的全限定名来过去定义此类的二进制流

      2)将这个字节流所代表的静态存储结构转化为方法区的运行时的数据结构

      3)在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区原来的类里面数据的入口

    而JVM并没有详细规定这三个步骤的详细实践,例如第一条的二进制流的来源就没有确定,因此诞生了很多我们熟知的引入类的方法:

      • 从ZIP包获取,对应的技术是的JAR、EAR、WAR包引入的基础,各种框架引入外部包的技术基石
      • 从网络中获取,对应的是Applet(一种类似于JavaScript应用的技术,技术上完全不相似,sun公司以前希望其作为浏览器上程序的运行环境)
      • 运行时计算生成,对应的是动态代理技术
      • 由其他文件生成,对应JSP应用里的各种应用

    需要特别说明的是,加载阶段还没有结束的时候,连接阶段就已经开始了  

  2.验证

    验证时连接的第一步,是为了确保Class文件的字节流中的包含信息符合当前虚拟机的要求,并不会对虚拟机危险。

    JVM标准并没有对此提出标准化步骤,但一般的JVM实现会有以下四个过程:

      1)文件格式验证,判断是否符合格式规范

        这个阶段会验证格式是否正确、是否符合版本要求、编码是否是UTF-8等

      2)元数据验证,堆字节码进行语义分析

        这个阶段验证是否有父类(除了Object之外的类都应该有父类)、是否继承了不允许继承的类(如被final修饰的类)、是否实现了父类或接口中的所有方法

      3)字节码数据验证,对数据流和控制流进行分析,是最复杂的一个阶段

        这个阶段的实例有 类型是否匹配(比如声明一个int型变量却放入一个long)、跳转指令的正确跳转(保证循环分支的正确执行)、类型转换是否有效(子类对象赋值给父类可以反之不行)

      4)符号引用验证

        例如,符号引用通过字符串描述的全限定名是否能找到对应的类(JDBC用到很多),

        还有一个很重要的就是类、字段、方法的访问性(private\protected\public\default)是否可以被当前类访问

  3.准备

    这一阶段正式为类变量(satic修饰的部分)分配内存并设置类变量初始值,一般为0值。

    例如:  public static int value = 1;

    此时准备阶段会准备value在方法区中,值为0,在初始化阶段,才会将1赋值给value。但如果被final修饰,这一阶段就会给其赋值。

  4.解析

    将常量池中的符号引用替换为直接引用的过程。

  5.初始化

    将准备阶段的值改变为程序员指定的值。

    初始化阶段是执行类构造器<clinit>()方法的过程,这个方法是:

      1)编译器自动收集类中所有类变量的赋值动作和静态语句块合并产生的

      2)而且是先变量赋值,再静态语句块的顺序(不确定)

      3)并且在子类的<clinit>()方法执行前,父类其方法一定已经执行完毕了

    下面看一段程序:

      

    static class Parent {
        public static int A = 1;
        static {
            A = 2;
        }
    }

    static class Sub extends Parent {
        public static int B = A;
    }
    public static void main(String[] args) {
        System.out.println(Sub.B);
    }

    很明显,应该输出2,因为先在Parent中A赋值为1,又在static中变为了2,然后才执行子类中的赋值。

    但是我对上文中提到的2)顺序表示怀疑,因为以下代码运行结果为1。

    static class Parent {
        static {
            A = 2;
        }
        public static int A = 1;
    }

    static class Sub extends Parent {
        public static int B = A;
    }
    public static void main(String[] args) {
        System.out.println(Sub.B);
    }

    因此,《深入理解Java虚拟机》中提到的<clinit>()的静态代码块与变量赋值的顺序在此不作确定,个人在win8-64位机器上测试(HotSpot JVM)为与代码顺序有关,原书作者认为无关。

Java面试准备之JVM详细研究三(类加载机制)

时间: 2024-10-21 08:14:00

Java面试准备之JVM详细研究三(类加载机制)的相关文章

Java面试准备之JVM详细研究一(Java内存区域)

Object obj = new Object(); 主要参考资料:<深入理解Java虚拟机>,未经本人及原书作者同意禁止转载. JVM中的数据区域 JVM中给数据分了这么几个区域: 其中: 1.程序计数器: 程序计数器是一块较小的内存空间,为当前线程所执行的字节码的行号指示器.也就是说,程序再翻译成为字节码了之后,分支.循环.跳转.异常处理.线程恢复等功能都需要程序计数器来完成. (注意!)此区域存储的东西是执行的虚拟机的字节码指令的地址,如果为natvie方法(natvie:一个调用非Ja

关于Java面试知识点解析——JVM基础篇

跳槽时时刻刻都在发生,但是我建议大家跳槽之前,先想清楚为什么要跳槽.切不可跟风,看到同事一个个都走了,自己也盲目的开始面试起来(期间也没有准备充分),到底是因为技术原因(影响自己的发展,偏移自己规划的轨迹),还是钱给少了,不受重视. 准备不充分的面试,完全是浪费时间,更是对自己的不负责(如果title很高,当我没说).今天给大家分享下 Java面试知识点解析--JVM基础篇 1)Java 是如何实现跨平台的? 注意:跨平台的是 Java 程序,而不是 JVM.JVM 是用 C/C++ 开发的,是

JVM内存模型与类加载机制

一. java虚拟机的内存模型如图: 补习一下jvm内存模型中的各个组成部分 堆: 我们new出来的对象全部放在堆中,他是jvm所能够动态分配的最大的一块空间 优点: 内存动态分配,生命周期不必事先告诉编译器,由JAVA的垃圾回收线程动态回收 栈: 存取的速度仅次于CPU的寄存器,但是存在栈里面的数据大小必须是提前定义的比如大家都知道int 4个字节 jvm在调用某些方法时,方法的参数,以及局部变量就存放在栈里面,(因为它们符合内存空间已知的特性) 主函数在栈的最底部 栈是有线程特性的,每个线程

Java面试准备之JVM

介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明 程序计数器:看做当前线程所执行的字节码行号指示器.是线程私有的内存,且唯一一块不报OutOfMemoryError异常. Java虚拟机栈:用于描述java方法的内存模型:每个方法被执行时都会同时创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息.每一个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机中从入栈到出栈的过程.如果线程请求的栈深度大于虚拟机所允许的深度就报StackOverflowError, 如果

Java性能优化之JVM GC(垃圾回收机制)

Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行.当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成.GC优化很多时候就是减少stop-the-world 的发生. JVM GC回收哪个区域内的垃圾? 需要注意的是,JV

【转载】Java性能优化之JVM GC(垃圾回收机制)

章来源:https://zhuanlan.zhihu.com/p/25539690 Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行.当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成.GC优化很多时候就是减少stop-

JVM系列文章(四):类加载机制

作为一个程序员,仅仅知道怎么用是远远不够的.起码,你需要知道为什么可以这么用,即我们所谓底层的东西. 那到底什么是底层呢?我觉得这不能一概而论.以我现在的知识水平而言:对于Web开发者,TCP/IP.HTTP等等协议可能就是底层:对于C.C++程序员,内存.指针等等可能就是底层的东西.那对于Java开发者,你的Java代码运行所在的JVM可能就是你所需要去了解.理解的东西. 我会在接下来的一段时间,和读者您一起去学习JVM,所有内容均参考自<深入理解Java虚拟机:JVM高级特性与最佳实践>(

Java 虚拟机程序执行:02 虚拟机的类加载机制

虚拟机的类加载机制 虚拟机的类加载机制 类加载的时机 类的显式加载和隐式加载 类加载的过程 类的生命周期 加载 加载的 3 个阶段 分类 验证 准备 解析 初始化 类加载器 如何判断两个类 “相等” 类加载器的分类 双亲委派模型 类加载的时机 JVM 会在程序第一次主动引用类的时候,加载该类,被动引用时并不会引发类加载的操作.也就是说,JVM 并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次.那么什么是主动引用,什么是被动引用呢? 主动引用

Yii框架分析(三)——类加载机制及应用组件的管理、配置、访问、创建

Yii应用的入口脚本引用出了Yii类,Yii类的定义: class Yii extends YiiBase { } 由yiic创建的应用里Yii类只是YiiBase类的“马甲”,我们也可以根据需求定制自己的Yii类. Yii(即YiiBase)是一个“helper class”,为整个应用提供静态和全局访问入口. Yii类的几个静态成员:$_aliases : 存放系统的别名对应的真实路径$_imports :$_classes :$_includePaths php include paths