什么是JVM

一、初始JVM

源代码文件.java,通过javac编译,转化为.class文件,运行这个字节码文件,

JVM本质上是一个运行在计算机上的程序,职责是将字节码文件里的字节码指令,转化为机器码,然后机器码就可以交给计算机运营。

JVM 功能一 ,解释和运行,对字节码文件中的指令,实时的解释成机器码,让计算机执行。

功能二,内存管理,自动为对象、方法等分配内存;自动的垃圾回收机制,回收不再使用的对象。对比c语言方便多了,对于对象的回收,需要程序员手动写代码进行回收。

功能三,即时编译,对热点代码进行优化,

java 如果不做任何优化,性能不如C、 C++等语言

java 语言 将字节码指令 通过java虚拟机实时的,解释成机器码,交给计算机。但是支持跨平台。

C/C++ 语言,可以将源码文件直接转化为机器码,然后交给计算机直接运行,节省了一个解释的过程。不具备跨平台特性。

java为什么要这么做呢,是为了跨平台,同一份字节码指令,通过不同平台的java虚拟机解释为不同的,机器码,例如windows的机器码,Linux的机器码,这样就实现了跨平台。

由于JVM需要实时解释虚拟机指令,不做任何性能优化不如直接运行机器码的C、C++语言。

即时编译,字节码指令,转化为机器码后,计算机可以执行,虚拟机发现这段代码被多次执行,被认定为是热点代码,有必要进行优化,所以把转化为的机器码存储到内存中,当第二次这段代码被执行的时候,直接从内存中取出机器码,进行执行,这样就节省了解释的步骤,提高了性能。

java虚拟机如何判断这个是热点代码。

Java 虚拟机判定热点代码的方式有两种:

基于采样的热点判定

主要是虚拟机会周期性的检查各个线程的栈顶,若某个或某些方法经常出现在栈顶,那这个方法就是"热点方法"。这种判定方式的优点是实现简单;缺点是很难精确一个方法的热度,容易受到线程阻塞或外界因素的影响。

基于计数器的热点判定

主要就是虚拟机给每一个方法甚至代码块建立了一个计数器,统计方法的执行次数,超过一定的阀值则标记为此方法为热点方法。

Hotspot 虚拟机使用的基于计数器的热点探测方法。它使用了两类计数器:方法调用计数器和回边计数器,当到达一定的阀值是就会触发 JIT 编译。

方法调用计数器:在 client 模式下的阀值是 1500 次,Server 是 10000 次,可以通过虚拟机参数: -XX:CompileThreshold=N 对其进行设置。但是 JVM 还存在热度衰减,时间段内调用方法的次数较少,计数器就减小。

回边计数器:主要统计的是方法中循环体代码执行的次数。

常见的JVM:《Java虚拟机规范》,由Oracle制定,内容主要包含Java虚拟机在设计和实现时需要遵守的规范,主要包含class字节码文件的定义、类和接口的加载和初始化、指令集等内容。

《Java虚拟机规范》是对虚拟机设计的要求,而不是对Java设计的要求,也就是说虚拟机可以运行在其他的语言比如Groovy、Scala生成的class字节码文件之上。

官网地址:https://docs.oracle.com/javase/specs/index.html

总结:

1、JVM到底是什么?

JVM全程Java Virtual Machine,是一个运行在计算机上的程序,他的职责是运行Java字节码文件。

2、JVM的三大核心功能是什么?

内存的管理(也就是对象的创建和回收)、解释字节码指令为机器码,即时编译(优化性能)三大功能。

3、常见的JVM虚拟机有哪些?

常见的JVM有HotSpot、GraalVM、OpenJ9等,另外DrangonWell龙井JDK也提供了一款功能增强的JVM,其中使用最广泛的还是HotSpot虚拟机。

二、字节码文件详解

1、Java虚拟机的组成

2、字节码文件的组成

3、类的声明周期

4、类加载器

1、JVM的组成

字节码文件从磁盘读取,从网络中传输过来,Java虚拟机需要把这个文件加载到内存中,后续使用起来比较高效,这个过程就是通过类加载器ClassLoader,加载class字节码文件中的内容到内存中。所以Java虚拟机需要准备一块内存空间,要存放字节码中的类和接口。

存放类、对象内存区域,叫运行时数据区域,本质就是Java虚拟机所管理的内存。

字节码被加载到内存之后,执行引擎就要执行代码,这个时候就将字节码指令解释成为机器码,并且为了性能优化,采用了即时编译器,有些对象不在使用了,也会使用垃圾回收器,回收对象。

执行引擎会负责本地接口调用,同时本地接口也会创建对象。

类加载器

运行时数据区域

垃圾回收器,【即时编译器在原理篇】。解释器和本地接口属于虚拟机底层调用。

2、字节码文件的组成

字节码文件的组成-应用场景

int i = 0;

i = i++;

最终i的值是多少

Java的反射是如何实现的【原理篇】

2.1打开字节码文件

字节码文件中保存了源代码编译之后的内容,以二级制的方式存储,无法直接用记事本打开,使用NotePad++使用十六进制插件查看class文件。

左边是当前文件的地址,中间是整个十六进制的数据,右侧是编码之后的结果。

使用jclasslib工具查看字节码文件,

https://github.com/ingokegel/jclasslib

https://github.com/ingokegel/jclasslib/releases/tag/6.0.5

2.2字节码文件的组成

主要包含五部分:

基础信息:魔数、字节码文件对应的Java版本号、访问标识(public final等等)、父类和接口。

常量池:保存了字符串常量、类或接口名、字段名,主要在字节码指令中使用。

字段:当前类或接口声明的字段信息

方法:当前类或接口声明的方法信息字节码指令,也就是方法通过编译转成字节码指令

属性:类的属性,比如源码的文件名内部类的列表等。

字节码文件的组成

重点介绍基本信息、常量池、方法,对于字段和属性不那么重要。

基本信息

字节码文件的组成部分-Magic魔数

文件是无法通过扩展名来确定文件类型的,文件扩展名可以随意修改,不影响文件的内容。

软件使用文件的头几个字节(文件头)去校验文件的类型,如果软件不支持该种类型就会出错。

Java字节码文件,文件头是CAFEBABE,将这个文件头称为magic魔数。虚拟机加载这个文件,如果发现文件头不是几个字母,就会报错,这样就会保证加载的文件确实是字节码文件。

主副版本号

主副版本号指的是编译字节码文件的JDK版本号,主版本号用来标识大版本号,JDK1.2是46之后每升级一个大版本就加1,52-46=6,1.2+0.6=1.8,当前字节码文件的jdk版本号是1.8。副版本号是当主版本号相同时,区分不同版本的标识,一般只需要关心主版本号。版本号的主要作用是判断当前字节码文件的版本和运行的JDK版本是否兼容,例如编译字节码文件的版本是15,运行在jdk1.7,就无法运行。

举例,主版本号不兼容导致的错误

类文件错误的版本52.0,应为50

代码:

解决方案:

1、升级jdk,

2、将第三方依赖的版本号降低或者更换依赖,以满足JDK版本的要求。建议这种

小结:

常量池

方法

常量池

字节码文件中常量池的作用:避免相同的内容重复定义,节省空间。

常量池中的数据都有一个编号,编号从1开始。在字段或者字节码指令中通过编号可以快速找到对应的数据。

字节码的指令中通过编号引用到常量池的过程称之为符号引用

图片一

方法

就是当前类或者接口声明的信息,存放到字节码指令

int i =0

i = i ++

字节码指令

i++ 底层的字节码文件是,先把i=0 放到交换栈中,然后运行字节码指令 by,进行自加,最后再赋值给i,这个时候是把交换栈中的0 赋值给i,所以i=0。

对于++i 字节码文件是先在变量里进行自加,然后把i=1放到交换栈中,然后再把i=1赋值给i,所以i=1。

2.3字节码常用工具

使用idea里的插件jclasslib

使用arthas定位线上出现的字节码问题

相关推荐
琪露诺大湿1 小时前
JavaEE-多线程初阶(1)
java·linux·开发语言·jvm·数据库·java-ee·1024程序员节
威哥爱编程3 小时前
Java Z 垃圾收集器如何彻底改变内存管理
java·jvm·zgc
与海boy6 小时前
第七章 JVM对高效并发的支持
jvm
王佑辉6 小时前
【jvm】如何设置堆内存大小
jvm
MegaDataFlowers16 小时前
JDK、JRE、JVM之间的关系
java·开发语言·jvm
程序员阿鹏16 小时前
详解:单例模式中的饿汉式和懒汉式
java·开发语言·jvm·后端·单例模式·eclipse
梦城忆16 小时前
JVM基础(内存结构)
开发语言·jvm·python
AGi_19 小时前
Java基础-JVM
java·开发语言·jvm
生产队队长1 天前
JVM(HotSpot):GC之垃圾回收器的分类
jvm