本篇博客主要针对Java虚拟机的类加载机制,虚拟机字节码执行引擎,早期编译优化进行总结,其余部分总结请点击Java虚拟总结上篇 。

一.虚拟机类加载机制

概述

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

类加载的时机

类加载的时机不止一种:

  • 遇到new等字节码指令时会进行类加载
  • 反射调用时会进行类加载

在初始化时,若待初始化的类有父类则其父类先进行初始化(接口除外),并且先初始化包含main的主类。需要注意的是子类引用父类非final静态变量时,只初始化静态变量所在类,即父类,而引用final类型static变量不会引起任何初始化,因为其编译期间就已经储存在常量池中了。另外数组定义也是不会引发类的初始化。比如

Student[] stus=new Student[10];

是不会引起Student类的初始化的。

类加载的过程

加载过程

通过类的全限定名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构,在内存中生成一个代表类的数据访问入口的java.lang.Class对象。

验证过程

验证过程的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。主要有

  • 文件格式验证:验证魔数,主次版本号,常量类型等。
  • 元数据验证:是否有父类,是否继承了不该继承的类,抽象类是否实现了方法等。
  • 字节码验证:确保程序语义是合法的,符合逻辑的。如类型转换,跳转指令等。
  • 符号引用验证:对类自身以外的信息(常量池中的各种引用)进行匹配校验。

准备过程

正式为类变量分配内存并设置类变量初始值的阶段,只包括类变量而不包括实例变量和final类变量,而且仅仅只是初始化为0值。

解析过程

虚拟机将常量池内的符号引用转换为直接引用的过程。符号引用用一组符号来描述所引用的目标。而直接引用是直接指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。

初始化阶段

在初始化阶段真正开始执行Java程序代码(字节码),执行类的构造器<clinit>()方法,<clinit>()方法是由编译器自动收集所有类变量的赋值动作和静态语句块的语句合并而成,同一类中的静态块与类变量按顺序初始化,在同一个加载器下,一个类只会被初始化一次。

类加载器

实现通过一个类的全限定名获取描述此类的二进制字节流的代码模块称为类加载器。比较两个类是否相等,一定是在同一个类加载器的前提下进行的,否则哪怕Class文件都一样也不相等

类加载器的分类

  • 启动类加载器, 负责将存放在<JAVA_HOME>\lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。
  • 扩展类加载器,负责加载<JAVA_HOME>\lib\ext目录或java.ext.dirs系统变量指定的路径中的所有类库。
  • 应用程序类加载器,负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

双亲委派模型

双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

这样做的好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。

二.虚拟机字节码执行引擎

虚拟机的执行引擎自行实现,可以自行制定指令集与执行引擎的结构体系。

栈帧

栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机栈的栈元素。它储存了方法的局部变量表,操作数栈,动态链接,方法返回地址,对于活动线程来说,只有栈顶的栈帧才是有效的,称为当前栈帧,与其关联的方法叫做当前方法。

局部变量表

局部变量表存放方法参数和方法内部定义的变量。单位是slot(槽),最大可以达到32位。垃圾回收时,slot可以复用,将不使用的变量置为null是有意义的,方便垃圾回收。局部变量不像类变量,是没有初始值的。

JIT编译器

当虚拟机发现某个方法或代码块运行特别频繁时,就会把这些代码认定为 “Hot Spot Code”(热点代码),为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化,完成这项任务的正是 JIT 编译器。

方法返回地址

  • 遇到方法的返回指令-->正常完成出口
  • 遇到异常并且未处理-->异常完成出口,不会给上层调用者产生任何返回值

方法调用

方法在编译时并不确定方法的真实地址,而是一个符号引用,使得Java的动态扩展能力提升,在类加载过程甚至运行时才确定目标方法的直接引用。

解析

在类的解析阶段将一部分符号引用转换为直接引用,这部分符号引用代表的方法必须“编译期可知,运行时不变”,如静态方法,私有方法,实例构造器,父类方法。final方法也是。

分派

静态分派(与重载相关),依赖静态类型来定位方法执行版本的分派动作。自动转型顺序:char->int->long->float->double->Character->Serializable->Object->char...

动态分派(重写相关),找到操作数栈顶的第一个元素所指向的对象的实际类型,若常量池中的描述符和简单名称都相符,则返回直接引用,否则对其父类进行第二步。

动态分配的实现:

在类的方法区建立一个虚方法表提升效率,若子类未重写父类的方法,则子类的继承方法中地址和父类方法的地址是一样的,若重写了父类的方法,则子类的方法地址就会改变,指向自己实现的版本。如上图Son的clone方法没有被重写,指向的是Object父类的地址,而hardChoice方法被重写了,指向的是Son自己实现的地址。

动态类型语言

类型检查的主题过程在运行期而不是在编译期,如Python,Javascript,Ruby,PHP,与之相对的就是静态语言。

解释执行与编译执行

解释执行为边解释边执行,编译执行则是先将源代码编译成目标语言 (如: 机器语言) 之后通过连接程序连接到生成的目标程序进行执行。

基于栈的字节码解释执行引擎

  • 基于栈的指令集:Java编译器输出的指令流
  • 基于寄存器的指令集:x86汇编

三.早期编译器优化

编译器

三种编译器:

  • 前端编译器:把.java变成.class的过程,eg:Javac
  • 后端运行期编译器(JIT):把字节码变成机器码的过程,eg:Hotpot的C1,C2编译器
  • 静态提前编译器(AOT):直接把*.java变成机器码的过程,eg:GCJ(GNU Compiler for the Java)

解析与填充符号表

词法分析

标记是编译过程的最小元素,关键字、变量名、字面量、运算符都可以成为标记,词法分析就是将源代码的字符流转变为标记集合。

语法分析

语法分析是根据Token序列构造抽象语法树的过程。抽象语法树是用来描述程序代码语法结构的树形表示方法,每一个节点都代表着程序代码的一个语法结构:包,类型,修饰符等。

注解处理器

类似编译器的一种插件,如果插件对语法树进行了修改,编译器将回到解析及填充符号表的过程重新处理。

语义分析

对语法抽象树进行上下文有关性质的审查,如类型检查。

字节码生成

将前面各个步骤生成的信息转换成字节码写到磁盘中,类构造器<cinit>和实例构造器<init>就是在这个阶段添加到语法树中。

Java语法糖

  • 泛型与类型擦除:与C#不一样,Java的泛型是伪泛型,在生成的字节码中已经被替换成了原生类型了,会自动加上类型转换。
  • 遍历:自动转换为iterator遍历。
  • 装箱与拆箱:==运算在不遇到算数运算的情况下不会自动拆箱。equals方法不会处理数据的类型转换,而==会。

条件编译

编译器不会编译if到达不到的语句,也就是取消分支不成立的代码块,可以查看反编译后的代码验证条件编译。

Java 虚拟机总结给面试的你(中)相关推荐

  1. Java 虚拟机总结给面试的你(下)

    本篇博客主要针对Java虚拟机的晚期编译优化,Java内存模型与线程,线程安全与锁优化进行总结,其余部分总结请点击Java虚拟总结上篇 ,Java虚拟机总结中篇. 一.晚期运行期优化 即时编译器JIT ...

  2. java反射 虚拟机优化_面试官问我:Java反射是什么?我回答竟然不上来......

    每天凌晨00点00分,第一时间与你相约 每日英文 We all have moments of desperation. But if we can face them head on, that's ...

  3. 【2022最新Java面试宝典】—— Java虚拟机(JVM)面试题(51道含答案)

    目录 一.Java内存模型 1. 我们开发人员编写的Java代码是怎么让电脑认识的 2. 为什么说java是跨平台语言 3. Jdk和Jre和JVM的区别 4. 说一下 JVM由那些部分组成,运行流程 ...

  4. Java虚拟机中 类的加载过程

    Java中 类的加载过程 例如下面的一段简单的代码 public class HelloWorld {public static void main(String[] args) {System.ou ...

  5. 常用的java虚拟机_带你了解 JAVA虚拟机 面试必备

    Java运行时数据区: Java虚拟机在执行Java程序的过程中会将其管理的内存划分为若干个不同的数据区域,这些区域有各自的用途.创建和销毁的时间,有些区域随虚拟机进程的启动而存在,有 些区域则是依赖 ...

  6. 【java虚拟机序列】java中的垃圾回收与内存分配策略

    在[java虚拟机系列]java虚拟机系列之JVM总述中我们已经详细讲解过java中的内存模型,了解了关于JVM中内存管理的基本知识,接下来本博客将带领大家了解java中的垃圾回收与内存分配策略. 垃 ...

  7. 写给Java程序员的Java虚拟机学习指南

    大家好,我是极客时间<深入拆解Java虚拟机>作者.Oracle Labs高级研究员郑雨迪.有幸借这个专题的机会,能和大家分享为何Java工程师要学Java虚拟机?如何掌握Java虚拟机? ...

  8. Java虚拟机详解----JVM常见问题总结

    [正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾 ...

  9. Java虚拟机是什么

    http://boy00fly.iteye.com/blog/1095263 要理解java虚拟机,你首先必须意识到,当你说"Java虚拟机"时,可能指的是如下三个不同的东西: 1 ...

最新文章

  1. 请立即弃用 Notepad++,还有 5 款更牛逼的选择!
  2. 【Python基础】入门Pandas不可不知的技巧
  3. 如何提高生产力(二)、软件的开发与采购
  4. c#(asp.net/core)杂谈笔记
  5. word如何绘制斜线表头
  6. AT070TN92 群创工业液晶屏7寸宽温工控屏
  7. 安装pod遇到这种错误ERROR: While executing gem ... (Errno::EPERM) Operation not permitted - /usr/bin/xco
  8. 龟兔赛跑预测 【简单模拟】
  9. Android实践:做一个可视频交互的智能小车
  10. 亚洲众包网站悄然崛起:中国成最大“雇主”
  11. 自锁电路设计1.5V电压起
  12. 使用友盟推送,别名(alias)推送
  13. OSI七层参考模型(开放式系统互联)
  14. 计算机osta试题,OSTA试题库(参考).doc
  15. linux geoserver,geoserver安装
  16. SaaS | 金山软件西山居游戏工作室
  17. Centos7.5搭建本地Yum源【2022最新--保姆级部署】
  18. C++Qt开发——Mode View(模型视图)
  19. p-中位模型与pareto解集
  20. 展示制作的发票报销系统项目

热门文章

  1. Extjs之window异步拦截关闭事件beforeClose的实现
  2. Matcher 类中的group(),group(int i),groupCount()
  3. Elasticsearch 运维实战之1 -- 集群规划
  4. 【BZOJ】1105: [POI2007]石头花园SKA
  5. Centos 6.4 PPTP ×××搭建
  6. window.postMessage跨文档通信
  7. mac os 快捷键
  8. (四十三)java版spring cloud+spring boot+redis多租户社交电子商务平台-eureka集群整合config配置中心...
  9. 026-请问你怎么测试网络协议
  10. mysql原理~undo