JVM基础

Java SE Specifications (oracle.com)(虚拟机官网)

一、什么是JVM(初识JVM)

jvm本质是一个运行在计算机上的程序,他的职责是运行Java字节码文件。

JVM的功能

1、解释和运行

Java源代码文件编译成字节码文件,而Java虚拟机就是将字节码指令解释成机器码,最后机器码交给计算机运行。

2、内存管理

  • 自动为对象、方法等分配内存
  • 自动的垃圾回收机制,回收不再使用的对象

3、即时编译(提升Java性能最核心手段)

  • 对热点代码进行优化,提升执行效率

Java需要实时解释,主要是为了支撑跨平台特性。

虚拟机发现一段代码字节码指令被多次调用(即热点代码),就会认为这段代码要优化,主动将这段代码解释并优化成机器码,然后将这段机器码保存在内存中,再次执行时就直接调用保存的机器码。

常见的JVM

  • HotSpot(C++语言编写)虚拟机使用最广泛且最稳定。Oracle版和Open版功能上基本相同,但在垃圾回收上会有些许区别

HotSpot发展历程

二、字节码文件详解

JVM虚拟机的组成

从一个字节码文件(可来自磁盘的读取或网络传输过来)开始,JVM通过类加载器(ClassLoader)将字节码文件加载到内存中,Java虚拟机就要准备一块内存空间来存放这些字节码文件中的类、接口和对象,存放类和对象的区域就叫运行时数据区(JVM管理的内存)

虚拟机要使用执行引擎来执行代码,将字节码文件中的指令解释成机器码,同时使用即时编译器优化性能。由于HotSpot由C++语言编写不存在字节码文件中,就要调用本地接口(调用底层虚拟机实现,由C++编写的方法如native修饰的方法),执行引擎负责调用本地接口。

字节码文件的组成(概览)

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

1、解决面试难题

若从Java语法层面解答,就浮于表面啦~~  ╮(╯▽╰)╭。若是从字节码文件中的字节码指令来回答会让人觉得你对Java虚拟机有一定的理解👍。

2、解决工作中的实际问题——版本冲突

如果掌握字节码文件组成,就很容易解决

3、解决工作中的实际问题——系统升级

字节码文件打开方式

1、jclasslib

字节码文件保存了源代码编译后的内容,以二进制的方式存储,无法直接用记事本打开阅读。推荐使用jclasslib(专业的字节码文件查看器)。

直接在idea安装jclasslib Bytecode Viewer插件即可

安装完重启idea,在view(视图中)看到Show Bytecode With Jclasslib就安装成功啦~~。😀

要想查看某个类的字节码文件要先打开这个类,然后点击Show Bytecode With Jclasslib才能查看到对应的字节码文件。

需要注意的是源代码发生变化要重新编译。

2、阿里arthas

Arthas是一款线上监控诊断产品,通过全局视角实时查看应用load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,大大提升线上问题排查效率。

先下载安装arthas(去看arthas API文档叭~)

在下载的目录下打开命令行输入java -jar arthas-boot.jar命令查看arthas版本和运行的Java进程

输入编号5,进入程序内部查看基本信息(内存,字节码文件,CPU负载等等)。

arthas功能

arthas命令

字节码文件组成

1、基础信息:包含魔数(没打错字)、字节码文件对应的Java版本号访问标识(public final等等)、父类和接口。基础信息分成两部分一个是一般信息,另一个是接口。

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

3、字段:将源代码中的字段信息保存到了字节码文件中(即当前类或接口的字段信息)。

4、方法:将源代码中具体内容转换成字节码指令。

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

详解字节码文件

1、基本信息

Magic魔数:隐含的,在jclasslib中看不到,每一个字节码文件都会携带魔数。

  • 文件时无法通过文件扩展名来确定文件类型的,文件扩展名可以随意修改,不影响文件的内容。
  • 软件使用文件的头几个字节(文件头)去校验文件的类型,如果软件不支持该种类型就会出错。

只修改了扩展名,原始文件不会变化,如.png(图片)改成了.avi用播放器打开会出错,因为文件头不同,播放器无法识别。

在Java文件中这个文件头就称为magic魔数,Java字节码文件前四个字节必须是CAFEBABE,Java虚拟机在加载字节码文件过程中发现前几个字节不满足CAFEBABE要求,就会立即报错,这样就保证了文件的安全性。

主次版本号是指编译字节码文件的JDK版本号,主版本号用来标识大版本号,JDK1.0-1.1使用45.0-45.3,JDK1.2是46之后没升级一个大版本号就加1;次版本号是当主版本号相同时作为区分不同版本的标识,一般只需要关心主版本号。

版本号的作用主要是判断当前字节码的版本和运行时的JDK是否兼容

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

由于字节码文件是52(即52-44=JDK8),而我的运行时环境是50(即50-44=JDK6),导致低运行环境去运行高字节码文件就报错啦~

解决的两种方案

  • 升级JDK版本(容易引发其他的兼容性问题,并且需要大量的测试)。
  • 将第三方依赖的版本号降低或更换依赖,以满足JDK版本的要求(建议这种解决方案)。

2、常量池

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

在常量池中8号编号

但是为什么要这么绕呢?

a1引用了常量池8中的内容,然后通过10去引用到字符串字面量abc

对于变量为abc,它的名字直接引用了常量池10中的abc(就是为了减少存储空间)

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

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

3、方法

操作数栈是临时存放数据的地方,局部变量表(是个数组)是存放方法中的局部变量的位置。

iconst_0:将常量0放入操作数栈中。

istore_1:会将操作数栈的内容弹出来,放入局部变量表1的位置。

iload_1:将局部变量表1中的数据放入操作数栈中去。(是将数据复制到操作数栈中,局部变量1数据没有消失)。

iadd:将操作数栈顶部的两个数据进行累加,结果放入栈中。

return:方法结束,返回。

那0存放什么?

显然存放的是main方法的参数args。

i++和++i结果不同就是iinc 1 by 1 和iload_1的顺序不同造成的。

字节码常用工具:javap -v命令

javap是JDK自带的反编译工具,可以通过控制台查看字节码文件的内容。适合在服务器上查看字节码文件内容。

 javap -v 文件路径(.class文件路径)> 输出位置(将字节码文件放入这个文件中)

查看字节码文件

  • 本地文件可以使用jclasslib工具查看,开发环境用jclasslib插件。
  • 服务器上文件用javap命令直接查看,也可用arthas的dump命令导出字节码文件到本地再查看。还可用jad命令来反编译出源码。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值