转载自:http://www.newsmth.net/nForum/#!article/Java/275101?p=2
经常听到过这种说法:如果一个对象没有被RC Roots直接或间接引用,就会被GC. 如果显示地把它设成null,可以帮助GC。是否真的有用?
分析:
HotSpot解释执行代码时,枚举GC Roots时,OopMap信息来源于类加载时的分析结果,由于这阶段基本没有对bytecode做过优化,OopMap的数据是比较粗糙的,由变量作用域范围,或者在说局部变量表中的存活时间所得出。PS:对OopMap和GC对对象是否可回收的判定,可以参考这篇文章: http://icyfenix.iteye.com/blog/1166660。
就这个例子来说,可以理解为直到System.gc()执行的那一刻,局部变量表中还有对placeholder的引用,因此在GC前的”=null”操作,实际是移除掉局部变量表中的placeholder引用,所以有”=null”的版本成功回收掉64M内存。
那实验看起来不是证明”=null”某些情况下是有用的吗?实际上前面说了,”=null”作用仅仅是打断局部变量表中的引用。而做到这点并不一定非得placeholder = null,把这句替换成“int a = 1”也能达到效果,反正就把那个坑占了,不在乎扔进去的是“null”还是“1”。
最关键的是,上面实验建立在“未JIT的前提下”,在JIT编译器进行控制流和数据流分析后,生成的OopMap就提供比较精确的信息,不需要通过”=null”来告知对象使命已经完成。退一步说,这时即使有”=null”操作,也会被优化掉,生成出来的本地代码与没有”=null”操作的版本是一模一样的。对于在意性能的代码,必定是执行频率高,会被JIT的,而不会被JIT的,也不需要在意效率,因此”=null”没有意义。
经常听到过这种说法:如果一个对象没有被RC Roots直接或间接引用,就会被GC. 如果显示地把它设成null,可以帮助GC。是否真的有用?
public static void main(String[] args)() {
byte[] placeholder = new byte[64 * 1024 * 1024];
System.gc();
}
在HotSpot VM、未JIT的前提下,GC日志为:
[GC 66846K->65824K(125632K), 0.0032678 secs]
[Full GC 65824K->65746K(125632K), 0.0064131 secs]
public static void main(String[] args)() {
byte[] placeholder = new byte[64 * 1024 * 1024];
placeholder = null;
System.gc();
}
在HotSpot VM、未JIT的前提下,GC日志为:
[GC 66401K->65778K(125632K), 0.0035471 secs]
[Full GC 65778K->218K(125632K), 0.0140596 secs]
分析:
HotSpot解释执行代码时,枚举GC Roots时,OopMap信息来源于类加载时的分析结果,由于这阶段基本没有对bytecode做过优化,OopMap的数据是比较粗糙的,由变量作用域范围,或者在说局部变量表中的存活时间所得出。PS:对OopMap和GC对对象是否可回收的判定,可以参考这篇文章: http://icyfenix.iteye.com/blog/1166660。
就这个例子来说,可以理解为直到System.gc()执行的那一刻,局部变量表中还有对placeholder的引用,因此在GC前的”=null”操作,实际是移除掉局部变量表中的placeholder引用,所以有”=null”的版本成功回收掉64M内存。
那实验看起来不是证明”=null”某些情况下是有用的吗?实际上前面说了,”=null”作用仅仅是打断局部变量表中的引用。而做到这点并不一定非得placeholder = null,把这句替换成“int a = 1”也能达到效果,反正就把那个坑占了,不在乎扔进去的是“null”还是“1”。
最关键的是,上面实验建立在“未JIT的前提下”,在JIT编译器进行控制流和数据流分析后,生成的OopMap就提供比较精确的信息,不需要通过”=null”来告知对象使命已经完成。退一步说,这时即使有”=null”操作,也会被优化掉,生成出来的本地代码与没有”=null”操作的版本是一模一样的。对于在意性能的代码,必定是执行频率高,会被JIT的,而不会被JIT的,也不需要在意效率,因此”=null”没有意义。