JVM局部变量表

java中方法的局部变量是放在虚拟机栈的局部变量表里面:

 

Java代码 复制代码  收藏代码
  1. public static void main(String[] args) {   
  2.     byte[] waste = new byte[6 * 1024 * 1024];   
  3.     int new_var = 0;   
  4.     System.gc();   
  5. }  
public static void main(String[] args) {
	byte[] waste = new byte[6 * 1024 * 1024];
	int new_var = 0;
	System.gc();
}

 

 上面的代码反编译后得到:

 

Java代码 复制代码  收藏代码
  1. public static void main(java.lang.String[]);   
  2.     flags: ACC_PUBLIC, ACC_STATIC   
  3.     Code:   
  4.       stack=1, locals=3, args_size=1  
  5.          0: ldc           #2                  // int 6291456   
  6.          2: newarray       byte  
  7.          4: astore_1   
  8.          5: iconst_0   
  9.          6: istore_2   
  10.          7: invokestatic  #3                  // Method java/lang/System.gc:()V   
  11.         10return  
  12.       LineNumberTable:   
  13.         line 30  
  14.         line 45  
  15.         line 57  
  16.         line 610  
public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=1
         0: ldc           #2                  // int 6291456
         2: newarray       byte
         4: astore_1
         5: iconst_0
         6: istore_2
         7: invokestatic  #3                  // Method java/lang/System.gc:()V
        10: return
      LineNumberTable:
        line 3: 0
        line 4: 5
        line 5: 7
        line 6: 10

 可以看到locals=3,也就是说局部变量表长度是3,有man函数的参数,waste变量和new_var三个变量。

在JVM参数:-verbose:gc -Xmx12m -Xms12m 下GC日志:

[GC 6514K->6448K(11776K), 0.0011241 secs]

[Full GC 6448K->6356K(11776K), 0.0070312 secs]

这个看起来很正常,局部变量表引用了waste和new_var,所以他们的内存没有被释放。

我的疑问是,在执行System.gc()之前,waste变量的作用范围已经过期了,从int new_var = 0开始,后面就再也没有用到waste,那gc为什么不能回收waste的空间(虽然局部变量表保存了waste的引用)。


其实这个在《深入理解java虚拟机》上面有详细的讲解p202页。
如果你把方法体写成这样就会回收了:

引用

{
byte b = new byte[6 * 1024 * 1024];
}
int a = 0;

这样垃圾回收器就会马上回收。
原因:局部变量表中的slot是可重用的,方法体中定义的变量,其作用域不一定会覆盖整个方法体,如果当前字节码的PC计数器的值已经超出了整个变量的作用域,那么这个变量对应的slot既可以交给其它的变量使用。
对照上面的代码int a = 0;变量a的定义已经超过了b的作用范围,所以a会重新使用b的slot(这个slot指的是局部变量表的基本单位),这时执行垃圾回收就会回收b所占用的空间。 


byte[] waste = new byte[6 * 1024 * 1024]; 
    int new_var = 0; 
    System.gc();

当你声明了wate数组后,再执行System.gc,即如下代码:
byte[] waste = new byte[6 * 1024 * 1024]; 
System.gc();

虚拟机是不会回收掉waste数组的,按理说都虚拟机都退出了,为什么没有清除waste呢?
那是因为在执行gc前,waste还在当前main方法的作用域中,虚拟机是不敢贸然回收waste的

如果在waste后加上 int new_var = 0;  这句话,还是没有执行回收内存呢?这里提出一个概念:
    局部变量表中的solt槽在局部方法中不同的作用域范围是可以重用的。
注意要在不同的作用域内是可重用的,也就是说如下代码是不可重用局部变量solt的

byte[] waste = new byte[6 * 1024 * 1024]; 
int new_var = 0; 

而这个片段是可以重用局部变量solt的
{
    byte[] waste = new byte[6 * 1024 * 1024]; 
}
int new_var = 0;
因为waste和new_var不在同一个作用域,对于上述代码,我们知道对于静态的main方法,局部变量表有3个solt
solt0  对应String[]参数
solt1  对应waste参数
而new_var 对应solt1,即solt重用了,当然waste在局部变量表中编程了GCRoots不可达的状态,这个时候执行gc
肯定就回收了waste占用的堆内存,相反,对于代码
byte[] waste = new byte[6 * 1024 * 1024]; 
int new_var = 0;  new_var并没有重用waste的solt,这个时候局部变量表保持着对waste的引用,gc当然不会回收


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值