java 强引用内存泄漏_【java】强软弱虚四种引用和ThreadLocal内存泄漏

一、强软弱虚

1、强引用:平时new出来的对象,只要有引用在,即使发生GC也回收不了。

public class M {

@Override

protected void finalize() throws Throwable {

System.out.println("finalize");

}

}

finalize()方法,当这个对象被回收时,会调用finalize()方法。

public class T01_NormalReference {

public static void main(String[] args) throws IOException {

M m = new M();

System.gc();

System.out.println(m);

m = null;

System.gc(); //DisableExplicitGC

System.out.println(m);

System.in.read();

}

}

执行main方法,结果如下: 可以看出第一次执行System.out.println(m);之后对象还在。证明了只要引用在,即使发生了GC也回收不了该对象。m=null,把引用断掉,此时发生GC,对象被回收掉了。

043506f3a3810c96f8ac0918dcda13e4.png

2、软引用:空间不够就回收,软引用适合做缓存,空间足够就放在那,空间不够就回收。

vm options: -Xms20M -Xmx20M   设置最大堆最小堆20M。

public class T02_SoftReference {

public static void main(String[] args) {

//10M

SoftReference m = new SoftReference<>(new byte[1024*1024*10]);

//m = null;

System.out.println(m.get());

System.gc();

try {

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(m.get());

//11M

//再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉

byte[] b = new byte[1024*1024*11];

System.out.println(m.get());

}

}

执行main方法,结果如下:最大堆,最小堆为20M,首先分配了一个10M的byte数组对象,然后执行gc操作,可以看到byte数组对象没有被回收,当尝试分配一个11M的数组时,heap装不下时,这时系统会垃圾收回,把软引用干掉。

8dc2eebe165a961c456884e840b8fe2c.png

引用关系如下:

9cd82d9f4ad41559ff462e8aff494c71.png

3、弱引用:遇到GC就会回收,解决ThreadLocal内存泄漏的问题。

public class T03_WeakReference {

public static void main(String[] args) {

WeakReference m = new WeakReference<>(new M());

System.out.println(m.get());

System.gc();

System.out.println(m.get());

// ThreadLocal tl = new ThreadLocal<>();

// tl.set(new M());

// tl.remove();

}

}

执行main方法,结果如下:gc之后,垃圾被回收。

112b981cb76ca94b9c25348bb2944ee5.png

引用关系如下:

390ff288687523e4de5cdae09c0c7a6b.png

弱引用只要遭遇垃圾回收,就会被回收掉。可能有人认为这弱引用一GC就被回收,一GC就被收回,那还有啥用啊?如果有另外一个强引用指向大M的时候,只要这个强引用消失掉,大M就应该去被回收。一般用在容器。最典型的应用就是ThreadLocal。

df8613c51c101b03b70e94dbf5b5dbd5.png

ThreadLocal内存泄漏:

public class ThreadLocal2 {

static ThreadLocal tl = new ThreadLocal<>();

public static void main(String[] args) {

new Thread(()->{

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

tl.set(new Person());

}).start();

new Thread(()->{

try {

TimeUnit.SECONDS.sleep(2);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(tl.get());

}).start();

}

static class Person {

String name = "zhangsan";

}

}

先来看看Thread源码

d5416d16e26f8383be5fcdc43f53d5d5.png

可以发现,每一个Thread都会对应1个ThreadLocalMap成员变量(也称threadLocals),也就是说1个线程对应1个ThreadLocalMap。

再看看ThreadLocal中的源码

51d94cecce300f4a8dfceba03f7b52b4.png

c6eea66f5a772b809cbc9612442d3747.png

可以看出ThreadLocalMap类是ThreadLocal的静态内部类,而Entry是ThreadLocalMap的静态内部类。通过set方法可以看出,key为this,也就是ThreadLocal(entry中的key声明为弱引用),value是Object,也就是我们要存的值。

往ThreadLocalMap里设置的值是啥?和tl有啥关系?ThreadLocalMap每1个元素对应1个entry。1个entry包含一对key、value。key为  tl,value是person。t1=new ThreadLocal<>();此时是强引用

4070b5fcd55afdb97d57cbcb0003ef90.png

① 为什么Entry要使用弱引用?设置为弱引用的key能预防大多数内存泄漏的情况,若是强引用,即使t1=null,但key的引用仍然指向ThreadLocal对象,会导致entry内存泄漏。。如果key为

②为什么要remove?ThreadLocalMap的生命周期和当前线程一样长。ThreadLocal对象被回收,key的值变为null,则导致整个value再也无法被访问到。(也就是ThreadLocalMap中会存在key为null,但是value不为null)因此仍然存在内存泄漏。如果不用了,务必remove掉。若当前线程一直不结束或者作为线程池中的一员。重复使用线程(线程id可能不变,导致内存泄漏)

4、虚引用:管理堆外内存。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值