10年 Java程序员,硬核人生!勇往直前,永不退缩!

欢迎围观我的git:https://github.com/R1310328554/spring_security_learn 寻找志同道合的有志于研究技术的朋友,关注本人微信公众号: 觉醒的码农,或Q群 165874185

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

LocalVariableTable中的 Slot, 是存在复用现象的,这个我早就知道,但是,不太清楚是如何复用的。

  • Java语言规范与JVM规范都没有对Java语言具体要如何使用JVM的局部变量slot做太多限制,只是规定了参数要从下标为0开始的局部变量区传递而已。作用域不重叠的局部变量之间是否一定要复用局部变量区的slot,这纯粹是实现细节——复用也可以,不复用也完全符合规范。所以这种事情只能针对某个具体实现来讨论。假如题主是用Oracle/Sun JDK或者OpenJDK,那么用JDK自带的javap工具来看看不同样子的源码生成怎样的字节码就可以感受到差别了。
  • 在Oracle/Sun JDK与OpenJDK里的javac实现,分配局部变量slot的方式非常死板,纯粹看几个因素:
  • 声明顺序:先到先得;
  • 作用域:进入作用域时抢最靠前得坑,一离开作用域就放开这个坑,让后面的作用域的变量可以占坑;
  • 类型:long与double占俩相邻slot,其它类型占一个slot。

我用的Java版本是Hotspot ,如下,也是有这样的现象的。

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

一个关键点是作用域,什么是java中变量的作用域?它范围是,从定义变量的那一行开始,到对应的代码块结束的那一行。那么什么是代码块呢? 包含它的花括号的整个部分就是 一个代码块。

 

看一个例子,如下的代码:

    private static void test1() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        for (int i = 0; i < 3; i++) {
            int ia = 1;
            long long1 = 2;
            int ib = 3;
            long long2 = 555;
            System.out.println(" over = ");
        }

        ArrayList<Integer> array=new ArrayList<Integer>();
        array.add(1);
        for (int i=0;i<array.size();i++) {
            System.out.println(array.get(i));
            Integer ia=array.get(i);
            Integer ib=array.get(i);
            System.out.println(ia);
        }
//        int i = ia + ib;
    }

javap得到的字节码是:

 LocalVariableTable:
        Start  Length  Slot  Name   Signature
         9      20     1       ia   I
        13      16     2    long1   J
        16      13     4       ib   I
        21       8     5    long2   J
         2      33     0        i   I
        82      16     2       ia   Ljava/lang/Integer;
        91       7     3       ib   Ljava/lang/Integer;
        54      50     1        i   I
        43      62     0    array   Ljava/util/ArrayList;

 

Slot 值出现了重复的0,1,2... ,可见,Slot就是出现了复用。Slot的占用是按照变量在源码出现的顺序来的。 不过,奇怪的是,从上面的信息看来,Slot并不是按字节码信息LocalVariableTable表的顺序来的,Start,Length,Name,Signature都不是的。 ia占用1个slot,long1是2个(尽管long1的起始的slot还是2,但是我们从ib 的起始slot可以推测),long2 起始的slot是5,那么它占用了几个slot呢?从上面的字节码信息,我们并看不出上面东西呢,我们只能根据经验推测, 经验就是 

long与double占俩相邻slot,其它类型占一个slot

如果非要看到long2 占用了几个slot,那么就需要再在其对应的作用域中long2 后面创建另外的变量,那么然后就可以通过它后面的变量的起始slot 推测了。

另外,我测试的时候,发现如果变量定义的位置是作用域最后一行的话,也就是说如果定义了变量,后面没有其他代码了,那么它是不会出现在LocalVariableTable表中的。为什么会这样?我想是因为这个时候它就完全无用了吧。如果要让它出现在LocalVariableTable表中,那么只要在其后面随便写点什么代码就好了!

 

需要注意的是,如果我们的方法,整个就一个作用域,是不会出现slot复用的,因为无法复用啊,一个方法什么情况会出现多个作用域呢? 其实很简单,一个while循环,或者for,或者if.. else,或者switch等等, 还有就是单单一个 花括号 包围也可以。

 

参考:

https://www.zhihu.com/question/41694588

 

posted on 2019-03-05 12:48  CanntBelieve  阅读(1150)  评论(0编辑  收藏  举报