摘要:二验证验证主要是为了确保文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全。五初始化类的初始化阶段是类加载过程的最后一步,该阶段才真正开始执行类中定义的程序代码或者说是字节码。
关注我,每天三分钟,带你轻松掌握一个Java相关知识点。
虚拟机(JVM)经常出现在我们面试中,但是工作中却很少遇到,导致很多同学没有去了解过。其实除了应付面试,作为java程序员,了解我们写的java程序为什么能运行起来也是很有必要的。
我准备在接下来的一系列文章中,整理虚拟机的相关运行机制,让同学们对虚拟机有个整体的概念。(声明一下,文章内容基于周志明的《深入理解Java虚拟机》,也非常推荐同学们去读这本书)
先说一个知识点,咱们写的代码,都是.java文件,但是虚拟机只认.class文件,那么谁做的这个部分的转换呢?
看到JDK中Tools&Tool APIs了吗,其中的javac干了这件事。
好了我们回到正题,虚拟机是怎么把class文件加载到内存中并且执行使用的呢?
一个class从进入内存到被提出内存,总共经历了加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)这7个阶段,哪这7个阶段都干了啥呢?
一、加载
这个阶段虚拟机主要完成了3件事:
1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法去这个类的各种数据的访问入口。
我们可以看到,通过加载,class文件从目标路径转到虚拟机的方法区中,并且clss文件中的数据能被其他人访问了。
二、验证
验证主要是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全。
验证阶段包含4个阶段的检验动作:
1.文件格式检验,就是说传过来的字节流一定要符合Class文件的格式规范。
2.元数据验证,这个是对解析出的字节码进行语义验证,以保证其信息符合Java语言规范。
3.字节码验证,这个阶段将对类的方法体进行校验分析,保证被校验类方法在运行的时候,不会危害虚拟机。
4.符号引用验证,这个阶段校验发生在虚拟机将符号引用转换为直接引用的时候,也就是在解析阶段发生的。
有的同学看到这可能要疑问了,你这验证的4个步骤,都涉及了后面的流程,难道这些流程不是顺序的吗?
其实加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这个中顺序开始,但是开始不代表进行或者完成,就是这些阶段被调用的顺序是确定的,但是他们在什么时候结束是不一定的,这些阶段通常都是互相交叉混合进行的。宝宝起名网
这里面解析阶段的执行顺序是不确定的,这是为了支持java的运行时绑定(多态就是动态绑定的体现,编译时不知道变量指向的是父类还是子类,只有在运行时才去找时机类型的方法表,确定方法签名调用)。
三、准备
准备阶段的工作是给类变量分配内存并使之类变量初始值的阶段。
这里说明一个概念:
类变量:类变量是Class级别的变量,通常用static修饰,它是在类层面共享的变量。
实例变量:实例变量是跟着对象走的,每new一个对象,就有一套类变量。
public static int value = 123;
这句在准备阶段,就会给变量value赋值为0,而不是123 。这就是准备阶段干的工作,这也是你为什么可以不给实例变量赋值初值的原因(而局部变量必须赋初值以后会解释,这也跟虚拟机有关)
四、解析
解析阶段是虚拟机常量池内的符号引用替换为直接引用的过程。
怎么理解呢?你可以把这个符号引用当做虚拟机和class文件约定好的黑话,不管哪个虚拟机来了,都要用一套黑话他们才能交流。而他们说了什么呢,他们说了目标对象的各种信息,这些信息在各个虚拟机里描述都不一样,你可以把虚拟机理解为各个杀手组织,他们再收到同一句黑话描述暗杀对象的时候,在组织内部对对象的描述都不一样。
直接引用就是杀手组织内部翻译的黑话了,翻译出来的内容一般都包括目标对象的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机内存布局实现相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同,如果有了直接引用,那引用的目标必定已经在内存中存在。
五、初始化
类的初始化阶段是类加载过程的最后一步,该阶段才真正开始执行类中定义的Java程序代码(或者说是字节码)。
对于初始化阶段,虚拟机规范有且仅有5种情况必须立即对类进行初始化:
1.遇到new(使用new关键字实例化对象)、getstatic(获取一个类的静态字段,final修饰符修饰的静态字段除外)、putstatic(设置一个类的静态字段,final修饰符修饰的静态字段除外)和invokestatic(调用一个类的静态方法)这4条字节码指令时,如果类还没有初始化,则必须首先对其初始化
2.使用java.lang.reflect包中的方法对类进行反射调用时,如果类还没有初始化,则必须首先对其初始化
3.当初始化一个类时,如果其父类还没有初始化,则必须首先初始化其父类
4.当虚拟机启动时,需要指定一个主类(main方法所在的类),虚拟机会首选初始化这个主类
5.当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
以上就是今天的知识点,各位小伙伴get到吗?创作不易,望各位多多点赞收藏,有什么建议可以留言告诉我,我会积极采纳!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/75282.html
摘要:最终形成可以被虚拟机最直接使用的类型的过程就是虚拟机的类加载机制。即重写一个类加载器的方法验证验证是连接阶段的第一步,这一阶段的目的是为了确保文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 《深入理解Java虚拟机:JVM高级特性与最佳实践(第二版》读书笔记与常见相关面试题总结 本节常见面试题(推荐带着问题阅读,问题答案在文中都有提到): 简单说说类加载过...
摘要:实现这个口号的就是可以运行在不同平台上的虚拟机和与平台无关的字节码。类加载过程加载加载是类加载的第一个阶段,虚拟机要完成以下三个过程通过类的全限定名获取定义此类的二进制字节流。验证目的是确保文件字节流信息符合虚拟机的要求。 引言 我们知道java代码编译后生成的是字节码,那虚拟机是如何加载这些class字节码文件的呢?加载之后又是如何进行方法调用的呢? 一 类文件结构 无关性基石 ja...
摘要:虚拟机为了保证一个类的方法在多线程环境中被正确地加锁同步。但启动类加载器不可能认识这些代码。实现模块化热部署的关键则是它的自定义类加载器机制的实现。 概念区分:加载、类加载、类加载器 类加载是一个过程。 加载(Loading)是类加载这一个过程的阶段。 类加载器是ClassLoader类或其子类。 本文中的类的描述都包括了类和接口的可能性,因为每个Class文件都有可能代表J...
阅读 766·2023-04-25 17:33
阅读 3625·2021-07-29 14:49
阅读 2479·2019-08-30 15:53
阅读 3431·2019-08-29 16:27
阅读 2000·2019-08-29 16:11
阅读 1029·2019-08-29 14:17
阅读 2430·2019-08-29 13:47
阅读 2016·2019-08-29 13:28