JVM的运行数据区和过程
从最高的抽象看,JVM获取Java的字节码,从而输出一定的值或者改变自身的状态。
所以接下来有一些问题,如
- java bytecode 的格式是什么
- JVM是如何获得Java bytecode的
- JVM是怎么产生值和改变状态的
JVM如何改变状态
1 | class Text{ |
JVM如何改变状态,换句话说,是指JVM虚拟机在执行JAVA字节码时,环境怎么变化。我说下JVM在执行上面代码的时候,所产生的状态变化,带大家了解JVM的基本数据空间,和JVM的运行时数据空间的变化。
如何开始
我们知道,Java代码是由一个个类构成,通过创建对象,运行对象的方法,从而改变JVM的状态。而每一个类都会生成一个CLASS文件。你可以做一个测试。
创建一个HelloWorld.java文件,里面代码如下
1 | //Declaring HelloWorld class. |
使用”javac HelloWorld.java”编译文件
![](E:\Z\blog\source\picture\Screenshot 2023-08-05 223726.png)
将会看到
![](E:\Z\blog\source\picture\Screenshot 2023-08-05 223822.png)
在这个文档里,发现JVM开始时会创建一个initial类,通过这个类,初始化这个类,通过这个类,调用”public main”方法。
![](E:\Z\blog\source\picture\Screenshot 2023-08-05 224810.png)
初始化环境
当JVM运行字节码文件时时,首先要初始化环境(加载类),为之后的运行代码提供条件,有五步骤
- 加载:得到数据
- 验证:判断数据是否安全,能不能执行,有没有Bug
- 开辟空间:在方法区里,开辟一定的空间
- 引用转换:把符号引用变成直接引用
- 填空间:对第三部开辟的空间赋值
对于上面的程序,在JVM初始化时,会将”Text” 和”Main”的类信息存到方法区里,初始化单个线程对应的栈,本地方法栈,程序计数器,初始化堆。
运行代码
当初始化完成,JVM在方法区中找到”public static void main(String[] args)”方法后,运行方法里的代码,代码运行完成,JVM运行完成。
下面是上面程序在”public static void main(String[] args)”方法里的代码,我们看看JVM在执行这些代码时,引起的数据区域变化。
1 | int k = 1; |
方法开始运行时,会在数据区域的栈顶新建一个栈帧,可以理解成一个大型的变量(环境)。方法运行时,通过改变环境来实现一定功能。
普通数据类型
1 | int k = 1; |
环境变化如图
创建对象
1 | Text t = new Text(2); |
当创建对象时,JVM首先在方法区(存储类信息的地方)看有没有这个类,找到类后在堆区创建一个没有初始化的对象区,在调用构造器方法后,对象区初始化,之后将对象的引用赋给变量。
注意:对象中的方法是每个对象(属于一个类)共有的,如果每有一个对象就给创建它所有的方法,我认为太浪费,JVM官方不会这样弄。
调用方法
1 | int l = t.doubleY(); |
当调用方法时,Stack会新建一个栈帧(环境),在这个环境里面执行方法体中的代码,当代码结束时,丢弃环境(POP),将返回值赋值给最初调用的地方。
JVM的调试工具
对于一个解释器JVM,调试工具必不可少
jps
这是一个监测虚拟机状态的工具,为了使用它,首先我们定义一些进程。
1 | class Main{ |
1 | public class SwingDemo { |
jps -l
这个命令是输出运行进程对应的LVMID(进程ID)和主类名,如果是JAR包,输出路径
当运行上面两个类时,之后运行”jps -l”
![](E:\Z\blog\source\picture\Screenshot 2023-08-03 160417.png)
jps -q
输出LVMID,省略主类名称
![](E:\Z\blog\source\picture\Screenshot 2023-08-03 161118.png)
jps -m
输出调用进程时,传递的给方法”main”的参数
![](E:\Z\blog\source\picture\Screenshot 2023-08-03 161303.png)
jps -v
输出JVM启动时的参数,比如堆的大小等
![](E:\Z\blog\source\picture\Screenshot 2023-08-03 161750.png)
CLASS文件格式
还记的这个图吗
现在我们来讲解那个云朵部分,云朵里面包含有八个信息部分
- 类信息:类的签名和版本号
- 常量信息:常量指广义的常量,除开final修饰的常量,还有类名,方法名等
- 进入权限:表示类的权限,public,还是,private
- 继承信息:继承的父类
- 接口信息:实现的接口
- 方法的信息:方法名和方法的权限
- 属性信息:存些属性值,如方法的方法体部分