Weak Reference弱应用分析

什么是弱引用。

系统GC时,不管当前的内存够不够,都会回收掉,前提是外部没有强引用时。

为什么说弱引用存在内存溢出,怎么避免?

因为ThreadLcalMap的键使用的是弱引用,当外部强引用不存在时,发生GC后,这个键就会变成NULL,导致entry变成脏数据,从而导致内存溢出的风险。
虽然ThreadLocal底层在set,get,remove等做了一些脏entry检测,但是我们还是要以正确的姿势来使用。没有使用ThreadLocal时,调用remove方法。

演示如何出现内存溢出的情况

下面使用一个简单例子说明:

public class TestNum {
    private static ThreadLocal seqNum = new ThreadLocal() {
        public Integer initialValue() {
            return 0;
        }
    };

    public int getNextNum() {
        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }

    private static class TestClient extends Thread {
        private TestNum sn;
        public TestClient(TestNum sn) {
            this.sn = sn;
        }
        public void run() {
            seqNum = null;
            System.gc();
        }
    }

    public static void main(String[] args) {
        TestNum sn = new TestNum();
        TestClient t1 = new TestClient(sn);
        t1.start();
    }
}
image.png

发生GC后,referent变成null


image.png

此时,ThreadLocalMap的数据就会存在这么一条记录,这是一条脏数据了。为什么会发生这样的情况呢?那么以下图说明


image.png

那么如何来避免内存溢出情况
当不在使用ThreadLocal时,执行它的move方法。

底层是否有做防止内存溢出的措施吗?

底层有一个方法expungeStaleEntry进行处理,这里不做分析。

既然弱引用存在内存溢出,为什么还要使用?

有些场景比较适合,如:线程会话管理,数据库连接管理等。

手写一个弱引用。

思路,既然ThreadLocal是自己实现一个类似Map来存储线程和值的。那么我们也可以用一个Map来实现。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class WeakReferenceConcurrentHashMap {
    private Map map = new ConcurrentHashMap();

    protected T initialValue(){
        return null;
    }

    public void set(T value){
        map.put(Thread.currentThread(), value);
    }

    public T get(){
        T result = null;
        result = map.get(Thread.currentThread());
        if(result==null && !map.containsKey(Thread.currentThread())){
            result=initialValue();
            set(result);
        }
        return result;
    }

    public void remove(){
        map.remove(Thread.currentThread());
    }
}

public class TestWeakReferenceConcurrentHashMap {
    private static WeakReferenceConcurrentHashMap seqNum = new WeakReferenceConcurrentHashMap() {
        public Integer initialValue() {
            return 0;
        }
    };

    public int getNextNum() {
        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }

    private static class TestClient extends Thread {
        private TestWeakReferenceConcurrentHashMap sn;
        public TestClient(TestWeakReferenceConcurrentHashMap sn) {
            this.sn = sn;
        }
        public void run() {
            for(int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getId() + " " + sn.getNextNum());
            }
        }
    }

    public static void main(String[] args) {
        TestWeakReferenceConcurrentHashMap sn = new TestWeakReferenceConcurrentHashMap();
        TestWeakReferenceConcurrentHashMap.TestClient t1 = new TestWeakReferenceConcurrentHashMap.TestClient(sn);
        t1.start();
        TestWeakReferenceConcurrentHashMap.TestClient t2 = new TestWeakReferenceConcurrentHashMap.TestClient(sn);
        t2.start();
        TestWeakReferenceConcurrentHashMap.TestClient t3 = new TestWeakReferenceConcurrentHashMap.TestClient(sn);
        t3.start();
    }
}

测试结果


image.png

你可能感兴趣的:(Weak Reference弱应用分析)