强软弱虚四种引用和ThreadLocal内存泄露

强软弱虚四种引用

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

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

***
 * 空间不够就会回收
 * 软引用适合做缓存(空间足够就放在那里 不够用会回收)
 * -Xmx=20M
 */
public class T02_SoftReference {

    public static void main(String[] args) throws Exception{
        /***
         * m指向SoftReference 对象
         * SoftReference 对象里面还有一个引用 是软引用 指向一个字节数组(10M)
         */
        SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
        System.out.println(m.get());
        System.gc(); /**gc不是在主线程 所以sleep 1s**/
        Thread.sleep(1000);
        System.out.println(m.get());
        //在new一个数组(强引用) heap将装不下,这时候系统会垃圾回收一次 如果不够 就把软引用
        byte[] b=new byte[1024*1024*15];
        System.out.println(m.get());
    }

软引用的内存结构(虚引用和弱引用的内存结构图也相似)

3)弱引用:遇到GC就会回收,解决ThreadLocal内存泄露的问题(参考下面ThreadLocal泄露)

  当这个弱引用指向对象M的时候,还有一个强引用执向M的时候,只要强引用消失掉,这个M对象就应该被回收(不需要在额外管理那个弱引用了),这就是弱引用的用处

    一般用在容器里,如:weekHashMap

4)虚引用:管理堆外内存,比如DirectByteBuffer(NIO的API 直接指向堆外内存 不需要Copy到JVM中 操作系统直接管理,JVM回收不了 0拷贝)

即:jvm内部可以访问操作系统管理的内存(用户空间可以管理内核空间的内存)

当某个引用被回收的时候 会通知队列(把信息填到对面里面当中),然后由GC线程清理堆外内存

public class T04_PhantomReference {
    public static final List<Object> LIST = new ArrayList<>();
    public static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();
    public static void main(String[] args) {
        PhantomReference<M> phantomReference=new PhantomReference<>(new M(),QUEUE);
        new Thread(() -> {
           while (true){
               LIST.add(new byte[1204*1024*10]);
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
                   Thread.currentThread().interrupt();
               }
               /**虚引用永远拿不到值*/
               System.out.println(phantomReference.get());
           }
        }).start();

        new Thread(() -> {
           while (true){
               Reference<? extends M> poll=QUEUE.poll();
               if (poll!=null){
                   System.out.println("虚引用被JVM回收了-----"+poll);
               }
           }
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

ThreadLocal内存泄露

1) ThreadLocal内部使用虚引用防止内存泄露:我们看ThreadLocal的set方法

ThreadLocal的set方法 后面的实现就是往Entry数组中 通过Entry的构造方法增加一个对象,

其中Entry构造里面调用了super(k),相当于用一个弱引用指向key。也就是指向ThreadLocal的引用是弱引用

Entry里面这个key是被一个弱引用指向的ThreadLocal对象

 ThreadLocal<M> t = new ThreadLocal<>();
        t.set(new M());

ThreadLocal的set方法 后面的实现就是往Entry数组中 通过Entry的构造方法增加一个对象,

如果我们用强引用的话:方法结束 ThreadLocal应该要回收掉

但是往线程t里面set的一个对象M,即使t为null了,Entry里面 依然会有一个强引用指向ThreadLocal(ThreadLocal对象既被t引用指向, 还被一个弱引用指向)

这个时候 因为Entry中的强引用会可能产生内存泄露(这个需要结合ThreadLocal的源码理解)

除非线程结束,但是有些线程是7X24小时不断开着的(比如NIO的Selector轮训)

2)ThreadLocal用完remove()防止内存泄露

因为源代码提示使用static关键字修饰ThreadLocal,

这时候:如果不进行remove()操作(remove就是把整个Entry从ThreadLocalMap里面删除) 上面t所持有的ThreadLocal对象也不会被释放的

一直set() 而不remove()也会引起内存泄露

所以:如果ThreadLocal被static修饰时,如果忘记remove() 也可能引起内存泄露

原文地址:https://www.cnblogs.com/ssskkk/p/12739557.html