从强、软、弱、虚四种引用到ThreadLocal使用及源码

1.各大培训机构,价格10万的视频 Java架构师视频免费送。

2.各种电子书籍经典Java书籍免费送

3.关注下方我的公众号进行免费获取。

4.个人技术总结免费赠送

 

 

​1.强软弱虚

 

 

    在聊ThreadLocal存不存在内存泄漏问题之前,我们先看看Java的4种引用,分别为强引用、软引用、弱引用和虚引用。

 

 

1.1 强引用

 

    强引用是我们可能接触最多的一种引用,例如我们写一个类Student stu = new Studnet,当stu指向Student对象时,这就是一个强引用,只要这个引用不消失都不会被垃圾所回收。

    重写finalize方法,只要一旦被回收时,就会调用此方法。

 

 

 

 

    需要注意的是System.gc(),只是通知垃圾回收器,并不是通知了马上就回收,什么时候被回收是无法手动控制的。

 

 

1.2  软引用(SoftReference)

 

    软引用(SoftReference),只要内存空间足够就不会被回收,一旦内存空间不够时,垃圾回收器就会回收掉软引用。

 

 

 上面代码中我们申请了一个10M大小的byte数组,然后再申请了一个10M大小的byte数组。前面10M的byte数组属于软引用,而后面再申请的byte属于强引用。

   ps:在测试软引用时,我们需要做一些设置,设置内存为15M,当前面的10M软引用申请后再申请时实际上空间已经不足,由于后面的是强引用,所以垃圾回收期会回收掉前面软引用。

 

 

    设置内存大小后,可以看到此时软引用已经为null,说明被垃圾回收给回收掉了。

 

 

 

1.3  弱引用(WeakReference)

 

    弱引用(WeakReference)只要垃圾回收器开始回收,只要发现弱引用就会直接回收掉弱引用。

    同样我们依然使用tudent对象并重写finalize方法。

 

 

    可以看到第一次去获取时是存在的,当我们通知完gc后,后面再次去获取的时候已经为null,说明已经被回收了

 

 

 

1.4  虚引用(WeakReference)

 

    当试图通过虚引用的get()方法取得强引用时,总是会返回null,并且,虚引用必须和引用队列一起使用。既然这么虚,那么它出现的意义何在?

    它的作用在于跟踪垃圾回收过程,在对象被收集器回收时收到一个系统通知。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,将这个虚引用加入引用队列,在其关联的虚引用出队前,不会彻底销毁该对象。所以可以通过检查引用队列中是否有相应的虚引用来判断对象是否已经被回收了。

 

 

 

    虚引用可以用在分配堆外内存上,例如NIO中,由于分配在堆外内存,无法直接被回收,所以可以通过虚引用的队列,当虚引用被回收后,然后通过队列中的信息来让堆外内存也进行回收。

 

 

2.ThreadLocal

 

    ThreadLocal是什么?我们通过下面代码来看一下,在第一个线程中将ThreadLocal中set了Student对象,第二个线程中我们尝试去拿第一个线程中set的Student对象发现为null。

 

 

 

    按照常理来说,两个线程使用的是一个ThreadLocal应该是可以get到的,为什么不行呢?原因是因为ThreadLocal对每一个线程都是独立的,如果使用的是相同线程则可以get到,如果不同则为null。

    例如Spring中的事务就是利用ThreadLocal来实现的。

 

 

3.ThreadLocal原理

 

    ThreadLocal能实现不同线程之间的隔离,实际上底层就是Map,这个Map存放的是key为ThreadLocal,value为所存放的值。同时这个Map在Thread中,不同的线程拥有不同的Map。

    ThreadLocal的set方法源码如下,可以看到是通过拿到当前线程,然后调用getMap拿到当前线程的Map,然后key为ThreadLocal,value为存放值。

     ps:ThreadLocal实现并不是key为当前线程,value为存放的值的Map。

 

 

 

 

    ThreadLocal的get方法源码如下,同样拿到当前线程的Map,然后直接通过ThreadLocal为key去Map中获取Vaule。

 

 

 

4.ThreadLocal内存泄漏问题

 

    ThreadLocal在正确使用的情况下不存在内存泄漏的问题,发生内存泄漏的原因是因为如果使用完了ThreadLocal但是没有调用remove方法则会产生内存泄漏问题。所以在使用完ThreadLocal后必须remove;如下所示。

 

 

    ThreadLocal之所以使用后不调用remove存在内存泄漏问题是因为ThreadLocal中的Map,Map中存放数据的Entry是一个弱引用。

 

 

 

    如果我们将new出来的ThreadLocal赋值为null时,也就是强引用消失时,当垃圾回收进行回收时会回收掉ThreadLocal,同时由于key为弱引用所以key也会被垃圾回收掉。但是如果key被回收掉了,那么value又是一个强引用不会被回收,此时完全不能通过key找到value。但是value却一直存在于内存中就会导致内存泄漏。

 

    总结:在使用ThreadLocal的时候必须在使用完毕后调用remove方法,防止内存泄漏。

 

 

1.各大培训机构,价格10万的视频 Java架构师视频免费送。

2.各种电子书籍经典Java书籍免费送

3.关注下方我的公众号进行免费获取。

4.个人技术总结免费赠送

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值