L1-深入拆解java虚拟机

课程地址

https://time.geekbang.org/column/article/11289

1.Java代码是怎么运行的

内容总结

  1. 字节码是啥?
    编译器将 Java 程序转换成该虚拟机所能识别的指令序列,也称 Java 字节码。
    之所以这么取名,是因为 Java 字节码指令的操作码(opcode)被固定为一个字节。

  2. jvm如何工作的?
    从虚拟机视角来看,执行 Java 代码首先需要将它编译而成的 class 文件加载到 Java 虚拟机中。
    加载后的 Java 类会被存放于方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。
    Java 虚拟机会将栈细分为面向 Java 方法的 Java 方法栈,面向本地方法(用 C++ 写的 native 方法)
    的本地方法栈,以及存放各个线程执行位置的 PC 寄存器。
    在运行过程中,每当调用进入一个 Java 方法,Java 虚拟机会在当前线程的 Java 方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。

例题

观察两个条件判断语句的运行结果,来思考 Java 语言和 Java 虚拟机看待 boolean 类型的方式是否不同。

  1. Foo.java

    1
    2
    3
    4
    5
    6
    7
    public class Foo {
    public static void main(String[] args) {
    boolean flag = true;
    if (flag) System.out.println("Hello, Java!");
    if (flag == true) System.out.println("Hello, JVM!");
    }
    }
  2. 编译并运行

    1
    2
    3
    4
    $ javac Foo.java    #将Foo.java编译为Foo.class(16进制的字节码文件)
    $ java Foo #jvm执行Foo.class文件
    Hello, Java!
    Hello, JVM!
  3. 基于asmtools将Foo.class转换成可编辑的文本文件

    1
    $ java -cp ~/asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm
  4. 修改asm中iconst_1值

    1
    2
    3
    $ awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm
    $ java -cp ~/asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm
    $ java Foo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
lixldeMacBook-Air:demo lixl$ cat Foo.jasm.1 

super public class Foo
version 52:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
public static Method main:"([Ljava/lang/String;)V"
stack 2 locals 2
{
iconst_1;
istore_1;
iload_1;
ifeq L14;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, Java!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L14: stack_frame_type append;
locals_map int;
iload_1;
iconst_1;
if_icmpne L27;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, JVM!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L27: stack_frame_type same;
return;
}

} // end Class Foo