前几天去面试,面试官问我:Java虚拟机是怎样运行Java字节码的?这个问题问的我哑口无言,虽然工作了5年,但是做的项目基本都是CRUD,所以只想能做好项目就可以了,管他什么底层,跟我有什么关系?
这次被打脸了,觉得很丢人,所以花了几天时间把JVM看了一遍,终于把Java虚拟机是怎样运行字节码的搞清楚了!
那我们先从HelloWorld 来开始字节码之旅;
Java文件是如何变成.class文件的
新建一个HelloWorld.java文件,代码如下
public class HelloWorld {
public static void main( String[] args ) {
System.out.println("Hello,World");
}
}
Java 从源文件到执行的过程如下图:
JDK的工具javac帮助我们把java文件编译成JVM可识别的class文件,在Java命令行中执行
javac HelloWorld.java,就可以看到生成的HelloWorld.class文件,
那么怎样看生成的16进制呢?
(1) window环境可以下载 winhex 16进制编辑器
(2) Linux环境下
- 查看class 文件 打开文件 vim HelloWorld.class ,然后输入:%!xxd 就是以16进制显示class文件了
- 也可以使用Linux下的xxd命令,将二进制信息转换为16进制数据,使用方式为
xxd HelloWorld.class HelloWorld.txt
那么如何查阅生成的字节码呢?
javap -verbose HelloWorld.class
public com.test.HelloWorld();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/tuling/test/HelloWorld;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello,World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
上面的转换是怎样操作的呢?
我们需要设计一个面向Java语言特性的虚拟机,并通过编译器将Java程序转换成该 虚拟机所能 识别 的指令 序列,也称为java字节码。
Java 为什么可以 一次编写,到处运行
Java虚拟机在各个平台(如Window_x64、Linux)上提供软件实现,这么做的意义呢?一旦一个程序被转换成Java字节码,那么它便可以在不同平台上的 虚拟机实现里运行。这就是我们常说的"一次编写,到处运行"。
Java虚拟机具体是怎样运行Java字节码的?
(1) 从软件层面上
执行Java代码,首先需要用 javac命令编译成class文件,然后由类加载器加载到虚拟机中,
Java后的java类会被存放于方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。
java 虚拟机会把栈细分为Java方法栈和本地方法栈,以及存放Java指令执行位置的PC寄存器。
下面我们举例说明代码执行过程:
对应的字节码如下:
执行图如下:
在运行过程中,每当调用进入一个 Java 方法,Java 虚拟机会在当前线程的 Java 方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且 Java 虚拟机不要求栈帧在内存空间里连续分布。
当退出当前执行的方法时,不管是正常返回还是异常返回,Java 虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。
那么中的动态链接是什么呢?
当程序在运行过程中,执行某一个方法(符号引用),找到方法里的代码
(2) 从硬件角度上
从硬件的角度上,Java 字节码是无法直接执行的。因此,Java虚拟机会根据不同的系统 将字节码转换为对应的机器码。
字节码是怎么转换为机器码的 呢?有 两种形式
第一种是解释执行,即逐条将字节码翻译成机器码并执行;
第二种是即时编译(Just-In-Time compilation,JIT),即将一个方法中包含的所有字节码编译成机器码后再执行。
前者的优势在于无需等待编译,而后者的优势在于实际运行速度更快。
HotSpot 默认采用混合模式,综合了解释执行和即时编译两者的优点。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。
如果对你有帮助,欢迎关注@码农的一天,转发!