美文网首页
深入拆解Java虚拟机——学习笔记(更新中)

深入拆解Java虚拟机——学习笔记(更新中)

作者: 本然酋长 | 来源:发表于2020-06-04 23:33 被阅读0次

第一讲 Java代码是怎么运行的

说实话,第一讲应该就是给大家铺垫一下,引到正课上,没啥好讲的。就是课后的练习做一做,算热身了。
代码粘在下面,方便说话。

$ echo '
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!");
 }
}' > Foo.java
$ javac Foo.java
$ java Foo
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1
$ awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm
$ java Foo

实践

它这个里面似乎是在linux下面操作的指令,不过我的系统是windows,就在win10下面勉强来吧,不是一次编译到处运行嘛。
编码,javac执行,都没啥问题。到了java Foo.java就出现问题了,跟我说没找到类库。先跳过吧,看看后面。这个asmtools.jar是什么东西?我以为是jdk里提供的工具,结果不是。网上搜了下,在openjdk中提供的一个工具,好像还要编译,找了现成的。尊重原著,出处:https://www.dazhuanlan.com/2019/12/21/5dfe225851355/
里面给出的下载链接:https://github.com/hengyunabc/hengyunabc.github.io/files/2188258/asmtools-7.0.zip
后面那个居然是awk命令,说实话没看懂是干什么的,放到linux虚拟机上执行了一遍,拿回来了。
最后再执行java Foo依然报错。这个不行,找找问题吧,没这么直接命令行编译过。首先是找不到执行主类,把命令改成java Foo Foo.class。然后报错如下:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.UnsupportedClassVersionError: Foo has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

嗯,jdk版本高了。我本地是jdk11,asmtools.jar应该是jdk8的。用jdk8再来一次吧。
第一次执行结果如下:

Hello, Java!
Hello, JVM!

第二次执行结果如下:

Hello, Java!
Hello, JVM!

这不应该呀,还是放到linux下面来一次吧。结果如下:
第一次执行结果:

Hello, Java!
Hello, JVM!

第二次执行结果:

Hello, Java!

分析改天写,先记录如此。

分析

我们看输出,也就是第二次里面,第二条语句没有执行。就是下面这句:

if(flag == true) System.out.println("Hello, JVM!");

区别就是,我们改过一次jasm文件。而jasm是java的字节码文件,也就是java虚拟机运行的文件。还是看下修改前后的区别吧:

前后字节码对比
上图中可以看到,只有一处不同。
不过呢,说实话,读不懂。为此,我查了下jvm字节码的语法链接是这里:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html
不过整体上来说,就是把那个boolean变量赋值为2了,就导致了之后的一系列结果。
具体的,我还解释不通,买了本书《深入理解JVM字节码》,等我解释得通了再回来翻译,接着看后面的课。

第二讲 JAVA的基本类型

在java虚拟机规范中,boolean被映射为了int,所以才有了上一节字节码修改的成立。
在本讲中对上一讲的作业进行了简单解释,但是我依然无法严格得揭示出所有代码。

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;  //这里后来被改成了iconst_2
        istore_1;
        iload_1;
        ifeq    L14;  //如果操作数栈上数值为0则跳转到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

java的基本类型

今天的作业是将下面这个代码:


public class Foo {
  static boolean boolValue;
  public static void main(String[] args) {
    boolValue = true; // 将这个true替换为2或者3,再看看打印结果
    if (boolValue) System.out.println("Hello, Java!");
    if (boolValue == true) System.out.println("Hello, JVM!");
  }
}

中的boolValue的值替换为2或者3,再试试。过程就不写了,结果是,两个判断里的内容都不出来了。

相关文章

网友评论

      本文标题:深入拆解Java虚拟机——学习笔记(更新中)

      本文链接:https://www.haomeiwen.com/subject/wftozhtx.html