zoukankan      html  css  js  c++  java
  • 跟随Javac代码来解答字节码的疑惑

      前言  

      本文是跟随掘金小册张师傅的《JVM字节码从入门到精通》练习而写的。

      问题

      问题一:

      有如下代码:

     1 package com.sun.tools.javac;
     2 
     3 /**
     4  * @author TY
     5  */
     6 public class Foo {
     7 
     8     public static void foo() {
     9         int a = 0;
    10         int b = 6;
    11         int c = 130;
    12         int d = 33000;
    13     }
    14 
    15 }

      很简单的一段代码,看似没有什么值得讨论的地方,然而将代码用javap查看下字节码:

      可以看到针对定义的不同的变量,字节码的指令是不同的,虽然都是整数。。。那为什么呢?简单分析就可以得知,是和

    声明的值的大小有关系,针对不同的大小范围采用不同的字节码指令。但是如何验证我们的想法呢?相信很多人是不知道的。下面会讲到如何验证

    我们的想法。

      问题二:

      再有如下代码:

     1 package com.sun.tools.javac;
     2 
     3 /**
     4  * @author TY
     5  */
     6 public class Switch {
     7 
     8     public static void foo() {
     9         int a = 0;
    10         switch (a) {
    11             case 0:
    12                 System.out.println("#0");
    13                 break;
    14             case 1:
    15                 System.out.println("#1");
    16                 break;
    17             default:
    18                 System.out.println("default");
    19                 break;
    20         }
    21     }
    22 
    23 }

      简单的一段swtich...case逻辑,看着也没有什么问题,再看看字节码:

      看到上面的字节码,采用的是looupswitch,而我们知道,switch...case语句底层有两种指令:tableswitch和looupswitch,之前我

    在学习的时候就只是知道tableswtich适用于case值比较紧凑的情况,而lookupswitch适用于case值比较稀疏的情况。但是上面的代码

    case值分别是0和1,,,这这这,算哪门子稀疏了,所以为什么要用lookupswitch而不用tableswtich呢?

      

      Javac源码

      我们知道将Java代码编译成字节码的命令是Javac,所以关于上面的问题,都应该在Javac的源码里面去找答案。

      首先下载Javac的源码,这里我提供张师傅提供的地址:javac-source-code-reading

      下载好之后用IDEA打开:

      

      上面的out目录是我自己建的,不然运行javac的入口main函数会报错让指定class码输出目录。

      新建了目录之后需要到项目设置里面设置:

      

       

      然后将com.sun.tools下面的两个包给排除掉,不然编译通不过

      

      这些做好之后就可以执行下javac的入口函数: com.sun.tools.javac.Main

      直接执行来看看:

      

       执行结果:

      

       好熟悉的输出,这不是在命令行里面直接执行javac的输出吗?

      

      所以,知道该怎么做了吧,需要加一个参数,参数就是要编译的java类的名字,我新建一个Hello类,随便写点啥。。。

      

      然后将Hello类的路径填写到Main的传入参数里面:

      

      再执行Main函数就可以看到Hello类的同级目录下就多了一个class文件:

      

       这就说明我们可以跟着Javac的源代码进行调试了,不过在此之前需要设置下:

      

      将Module source移到系统安装的JDK的前面,这样才能正常的调试而不是调试的时候跳转到安装的JDK里面去。

      可以调试Javac的代码意味着很多东西,我们遇到不理解的字节码的时候可以跟着Javac源码走一遍,看人家到底是怎么去实现的。

      接下来回到上面的两个问题,对应的Javac的源码分别在com.sun.tools.javac.jvm.Item类和com.sun.tools.javac.jvm.Gen类中,比如问题一代码对应下面:

      

      可以看到针对值得大小不同走了不同的逻辑:

      -1到5:iconst_0

      -128到127:bipush

      -32768到32767:sipush

      然后超过以上的范围则是:ldc

      而问题二对应的代码在Gen类visitSwitch方法中,重点代码是下面这段:

      

      hi和lo是在的代码逻辑中分别指向case的最大和最小值。我们将case值0和1代入其中:

      最终table_space_cost + 3 * table_time_cost = 15,而looup_space_cost + 3 * looup_time_cost = 13,

    所以最终选用了lookupswitch指令。关于为什么要采用这样的算法我暂时不知,但至少对照着Javac源码知道了算法逻辑,不至于遇到

    问题的时候一脸懵。。

      

      总结

      本文主要讲述了如何调试Javac源码以及如何运用Javac源码解答一些字节码的问题。

      另外,推荐掘金小册张师傅的小册《JVM字节码从入门到精通》

      可以扫描下面我分享的二维码购买,会便宜几块钱。。。(土豪不用管)

      

  • 相关阅读:
    java之jvm学习笔记五(实践写自己的类装载器)
    java之jvm学习笔记二(类装载器的体系结构)
    链式线性表
    java之jvm学习笔记十三(jvm基本结构)
    Java用户登陆界面
    李兴华JavaWeb开发笔记
    Java IO学习笔记:概念与原理
    Linux下一个简单的日志系统的设计及其C代码实现
    关于Core Location-ios定位
    C语言中main函数的參数具体解释
  • 原文地址:https://www.cnblogs.com/alinainai/p/12123622.html
Copyright © 2011-2022 走看看