强软弱虚(关键词: ThreadLocal,堆外内存)

2021-01-22  本文已影响0人  每皮1024

前言

强引用

普通常见的引用

示例

public class NormalReference {
    public static void main(String[] args) throws IOException {
        M = new M();
        m = null;
        System.gc();//DisableExplicitGC
        System.in.read();
    }
}

软引用

示例

软引用在内存够用时不会被回收,在内存不够发生GC时会被回收

// 前提:JVM启动时-Xmx设置为20M
public class SoftReference {
    public static void main(String[] args) throws IOException {
        // m在栈中,堆中有一个SoftReference对象,此引用为强引用;
        // SoftReference对象又有一个软引用指向一个byte数组,此引用为软引用
        SoftReference<Byte[]> m = new SoftReference<>(new byte[1024*1024*10]); //10M
        // 如果需要得到软引用的内容,使用软引用的get()方法即可
        System.out.println(m.get())//有输出,数组对象值

        System.gc();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(m.get());//有输出,数组对象值

        // 再分配一个数组,heap将装不下,此时会将软引用的对象给回收
        byte[] b = new byte[1024*1024*15];
        System.out.println(m.get());//null
    }
}

用途

缓存,例如常用大图片

弱引用

弱引用的对象,只要出现了GC就会被回收,适合ThreadLocal这种对象,因为不仅本身有强引用,在线程内部又会有key的弱引用,采用此引用可以保证强引用消失时能够被GC掉而不造成内存泄漏

示例

// 弱引用:被问到最多的一个
public class WeakReference {
    public static void main(String[] args) throws IOException {
        WeakReference<M> m = new WeakReference<>(new M());

        System.out.println(m.get());// 有输出
        System.gc();
        System.out.println(m.get());// null,一旦GC就直接回收了

        ThreadLocal<M> tl = new ThreadLocal<>();
        tl.set(new M());
        tl.remove();
    }
}

// ThreadLocal源码
public void set(T value) {
    Thread t = Thread.currentThread();
    // 根据当前线程对象获得一个map,然后往当前线程的map(threadLocals)里面放key value,注意key是ThreadLocal对象 
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}
ThreadLocalMap getMap(Thread t) {
  // 可以看到这个map实际上是线程自己的一个map
    return t.threadLocals;
}
private void set(ThreadLocal<?> key, Object value) {
    // We don't use a fast path as with get() because it is at
    // least as common to use set() to create new entries as
    // it is to replace existing ones, in which case, a fast
    // path would fail more often than not.
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        if (k == key) {
            e.value = value;
            return;
        }
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
    // 可以看到key value最终是一个Entry的形式放进threadLocals里的
    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}
// Entry本身继承自弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

用途

虚引用

示例

// 虚引用:永远get不到
// 虚引用:永远get不到
public class PhantomReference {
    private static final List<Object> LIST = new LinkedList<>();
    // 首先要有一个队列
    private static final ReferenceQueue<> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) throws IOException {
        // 虚引用pr,指向PhantomReference对象
        // PhantomReference对象,指向M对象,并且指明该虚引用所属队列是哪一个(QUEUE)
        PhantomReference<M> pr = new PhantomReference<>(new M(), QUEUE);

        // 此线程不断往加内存,使其膨胀,让垃圾回收器开始工作。(每次垃圾回收器回收一次,虚引用对象就会被回收)
        new Thread(() -> {
            while(true) {
                LIST.add(new byte(1024*1024));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                System.out.println(pr.get());// null
            }
        }).start();

        // 监控线程,监控QUEUE是否有内容,拿出来即表明堆外内存已经回收
        new Thread(() -> {
            while(true) {
                Reference<? extends M> poll = QUEUE.poll();
                if(poll != null) {
                    System.out.println("--- 虚引用对象被jvm回收了 ---" + poll);
                }
            }
        }).start();
    }
}

用途

待自学点

AtomicReference

上一篇下一篇

猜你喜欢

热点阅读