java中方法的局部变量是放在虚拟机栈的局部变量表里面:
public static void main(String[] args) { byte[] waste = new byte[6 * 1024 * 1024]; int new_var = 0; System.gc(); }
上面的代码反编译后得到:
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[] 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当然不会回收