java中list.clear()和list = null对于GC有没有帮助?

网上很多java性能优化的帖子里都会有这么一条: 尽量把不使用的对象显式得置为null.这样有助于内存回收
可以明确的说,这个观点是基本错误的. jdk远比我们想象中的机智.完全能判断出对象是否已经no ref…但是,我上面用的词是"基本".也就是说,有例外的情况.这里先把这个例外情况给提出来,后续我会一点点解释.这个例外的情况是, 方法前面中有定义大的对象,然后又跟着非常耗时的操作,且没有触发JIT编译…总结这句话,就是:除非在一个方法中,定义了一个非常大的对象,并且在后面又跟着一段非常耗时的操作.并且,该方法没有满足JIT编译条件,否则显式得设置 obj = null是完全没有必要的
上面这句话有点绕,但是,上面说的每一个条件都是有意义的.这些条件分别是

1 同一个方法中
2 定义了一个大对象(小对象没有意义)
3 之后跟着一个非常耗时的操作.
4 没有满足JIT编译条件

上面4个条件缺一不可,把obj显式设置成null才是有意义的. 下面我会一一解释上面的这些条件

同一个方法中

这个条件是最容易理解的,如果大对象定义在其他方法中,那么是不需要设置成Null的。

public class Test
{
     public static void main(String[] args){
     
         foo();
         
         System.gc();
     }
    
     public static void foo(){
         byte[] placeholder = new byte[64*1024*1024];
    }
}

对应的输出如下,可以看到64M的内存已经被回收

D:\>java -verbose:gc Test
[GC 66798K->66120K(120960K), 0.0012225 secs]
[Full GC 66120K->481K(120960K), 0.0059647 secs]

其实很好理解,placeholder是foo方法的局部变量,在main方法中调用的时候,其实foo方法对应的栈帧已经结束.那么placeholder指向的大对象自然被gc的时候回收了.

定义了一个大对象

这句话的意思也很好理解.只有定义的是大的对象,我们才需要关心他尽快被回收.如果你只是定义了一个 Integer i = new Integer(1); 后续手动设置成null让gc回收是没有任何意义的.

后面跟着一个非常耗时的操作

这里理解是:后面的这个耗时的可能超过了一个GC的周期.例如

public static void main(String[] args) throws Exception{
         byte[] placeholder = new byte[64*1024*1024];
         Thread.sleep(3000l);
         // dosomething
}

在线程sleep的三秒内,可能jvm已经进行了好几次ygc.但是由于placeholder一直持有这个大对象,所以造成这个64M的大对象一直无法被回收,甚至有可能造成了满足进入old 区的条件.这个时候,在sleep之前,显式得把placeholder设置成Null是有意义的. 但是,如果没有这个耗时的操作,main方法可以非常快速的执行结束,方法返回,同时也会销毁对应的栈帧.那么就是回到第一个条件,方法已经执行结束,在下一次gc的时候,自然就会把对应的"垃圾"给回收掉.

没有满足JIT编译条件

jit编译的触发条件,这里就不多阐述了.对应的测试代码和前面一样

public class Test
{
    public static void main(String[] args) throws Exception{
        byte[] placeholder = new byte[64*1024*1024];
        placeholder = null;
        //do some  time-consuming operation
        System.gc();
    }
}

在解释执行中,我们认为placeholder = null;是有助于对这个大对象的回收的.在JIT编译下,JIT编译器进行控制流和数据流分析后,生成的OopMap就提供比较精确的信息,不需要通过”=null”来告知对象使命已经完成.退一步说,这时即使有”=null”操作,也会被优化掉,生成出来的本地代码与没有”=null”操作的版本是一模一样的.

以下为个人粗浅的见解:
关于对象回收

一个对象没有引用便会被GC检索并标记为待回收的垃圾。然后等着被GC回收,在没被GC回收前,这个对象始终在内存中占据着空间。

关于list.clear() 和 list = null
List<Animal> list = new ArrayList<Animal>();
  • 先说list.clear();
    list.clear()只是将list里保存的指向各个animal的引用变量给清空,所以只是指向各个animal的引用被干掉了,各个animal对象并没有被销毁,依旧占据着空间,但是由于他们没有了引用,所以只能等着被GC回收掉。
  • 再说list = null;
    list = null意味着把list数组的引用置为空,由于list失去了引用(list对象并没有被销毁,只是没有了引用指向这个list对象,而且list对象里仍然保存着指向各个具体animal对象的引用),所以list对象GC不可达,同样也意味着list中指向的那些animal也变成了GC不可达(因为都没有了引用指向list,又怎么能指向list中保存的引用变量所指向的实例呢?),所以list和这些animal都变成了垃圾。但同样的,list对象和各个animal对象在堆上所占据的内存空间也没有被立刻清理掉,而是等着GC来回收掉他们时内存才会被释放。
  • 关于什么时候使用 list.clear() 或 list = null
    愚见:根据前文观点,本人认为只有在用list保存大对象,且list所在方法内list后面跟着很多代码很耗时的情况下,建议使用 list = null 将list的引用置为空,主动告诉GC这个list是垃圾。
  • 另,list.clear() 并没有像 list = null 一样将list引用干掉,所以若是之后还需要用这个list,建议使用list.clear()复用list对象,省得再创建一个了。
除个人愚见外转载自:

https://www.cnblogs.com/ouhaitao/p/9996374.html

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值