运行时栈帧结构-局部变量表-探究slot复用

局部变量表

    我们知道,一个方法的调用开始,相当于一个栈帧(Stack Frame)在Java虚拟机栈中的入栈和出栈。

    栈帧(Stack Frame)包括:局部变量表、操作数栈、动态链接、返回地址等。

    下图:每个线程对应一个Java虚拟机栈,Java虚拟机栈中有很多栈帧(Stack Frame)

03111359_Jp8j.jpg(图片来源:csdn-骁兵

    栈帧(Stack Frame)的局部变量表:存放方法参数局部变量的内存区域。

    java数据类型包括:byte boolean char short int long float double reference(对象引用数据类型,是指向堆内存的引用)

    栈帧(Stack Frame)的局部变量表中的最小单位为slotjvm规范中没有特定指定slot的大小,通常在32位操作系统中,slot占32位,此时long/double占两个slot。再64位操作系统中,slot占64位。所有数据类型都占1个slot。

    

slot复用

    当一个变量的pc寄存器的值大于slot的作用域的时候,slot是可以复用的。(抄的,我也不懂。所以自己做实验)

写一个测试类aa:

/**
 * Description:测试slot复用
 * 运行参数:-verbose:gc
 * 结果:大对象a不会被gc回收掉
 * 分析:由于在gc的时候,对象a还是可达对象(因为局部变量表slot1位置引用了对象a)。所以gc不会回收a
 * @author LiuXianfa
 * @date 2018/4/3
 */
public class aa {
    public static void main(String[] args) {
        byte[] a = new byte[60*1024*1024];
		int b= 10;
        System.gc();
    }
}


C:\Users\AnXiaole\Desktop>javac aa.java // 此命令:编译aa.java
C:\Users\AnXiaole\Desktop>javap -v aa // 此命令:反编译当前classpath下的aa类
// 这里省略常量池及其他信息
{
// 这里省略构造方法

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=1   // 注释:本地变量表使用了三个slot(locals=3)注意:slot0表示的是this对象的引用
         0: ldc           #2           // int 62914560
         2: newarray       byte
         4: astore_1                   // 注释:把第2步创建的newarray byte数组的引用放到slot1位置上
         5: bipush        10
         7: istore_2                   // 注释:把第5步放到操作数栈的数字10,放到slot2位置上
         8: invokestatic  #3           // Method java/lang/System.gc:()V
        11: return
      LineNumberTable:
        line 9: 0
        line 10: 5
        line 11: 8
        line 12: 11
}
SourceFile: "aa.java"
/**
 * Description:测试slot复用
 * 运行参数:-verbose:gc
 * 运行结果:大对象a会被gc回收掉
 * 分析:由于在gc触发的时候,对象a为不可达对象(反编译第7行可以看到:[slot1中指向堆中a对象的引用] 被[int 10]给覆盖了,所以gc触发时,堆中的a对象就变为不可达对象)。所以gc会回收a
 * @author LiuXianfa
 * @date 2018/4/3
 */
public class aa {
    public static void main(String[] args) {
        {
			byte[] a = new byte[60*1024*1024]; //注意,这里有个代码块。a的作用域就在代码块中
		}
		int b= 10;
        System.gc();
    }
}



C:\Users\AnXiaole\Desktop>javac aa.java

C:\Users\AnXiaole\Desktop>javap -v aa
Classfile /C:/Users/AnXiaole/Desktop/aa.class
// 这里省略常量池及其他信息
{
// 这里省略构造方法

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1   // 注释:本地变量表使用了2个slot(locals=2)注意:slot0放的是this对象的引用
         0: ldc           #2           // int 62914560
         2: newarray       byte
         4: astore_1                   // 注释:把第2步创建的newarray byte数组的引用放到slot1位置上
         5: bipush        10
         7: istore_1                   // 注释:把第5步放到操作数栈的数字10,放到slot1位置上
         8: invokestatic  #3           // Method java/lang/System.gc:()V
        11: return
      LineNumberTable:
        line 10: 0
        line 12: 5
        line 13: 8
        line 14: 11
}
SourceFile: "aa.java"
/**
 * Description:测试slot复用
 * 运行参数:-verbose:gc
 * 运行结果:大对象a不会被gc回收掉
 * 分析:虽然在gc的时候,已经在a的作用域之外,但是由于没有对slot进行写操作(就是gc触发时,slot1的位置还是指向对象a的应用,堆中的a对象仍然是可达对象),所以就不会把a回收掉。
 * @author LiuXianfa
 * @date 2018/4/3
 */
public class aa {
    public static void main(String[] args) {
        {
			byte[] a = new byte[60*1024*1024];
		}
		// int b= 10;  // 这里注释掉
        System.gc();
    }
}


C:\Users\AnXiaole\Desktop>javac aa.java

C:\Users\AnXiaole\Desktop>javap -v aa
Classfile /C:/Users/AnXiaole/Desktop/aa.class
// 这里省略常量池及其他信息
{
// 这里省略构造方法

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // int 62914560
         2: newarray       byte
         4: astore_1
         5: invokestatic  #3                  // Method java/lang/System.gc:()V
         8: return
      LineNumberTable:
        line 4: 0
        line 7: 5
        line 8: 8
}
SourceFile: "aa.java"
/**
 * Description:测试slot复用
 * 运行参数:-verbose:gc
 * 运行结果:对象a不会被gc回收掉
 * 分析:gc触发时,a仍然是可达对象。(局部变量表slot1位置引用了a对象)
 * @author LiuXianfa
 * @date 2018/4/3
 */
public class aa {
    public static void main(String[] args) {
        byte[] a = new byte[60*1024*1024];
		// int b= 10;
        System.gc();
    }
}


C:\Users\AnXiaole\Desktop>javac aa.java
C:\Users\AnXiaole\Desktop>javap -v aa
Classfile /C:/Users/AnXiaole/Desktop/aa.class
// 这里省略常量池及其他信息
{
// 这里省略构造方法

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // int 62914560
         2: newarray       byte
         4: astore_1
         5: invokestatic  #3                  // Method java/lang/System.gc:()V
         8: return
      LineNumberTable:
        line 3: 0
        line 5: 5
        line 6: 8
}
SourceFile: "aa.java"

 

编码建议

114120_Dl1x_2475326.png  

 

参考

    龙果学院 叶子猿 深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理)第96节 局部变量表

 

 

转载于:https://my.oschina.net/anxiaole/blog/1789098

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值