浅读java.util.Map及其实现类(五)


强引用,弱引用,软引用,虚引用


强引用

哪怕内存不足也不会回收的引用,真是真爱
一些常用的初始慢的数据可以放到强引用中,以免下次其他访客使用时再次进行漫长的初始 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

弱引用

只要被垃圾回收扫描到,就回收它。。
以下代码,我put了5000次,最后输出只有2000多。当然我特意修改了我的jvm内存配置
当然也可以吧内存调高了手动调用System.gc
static int i = 10;
	public static void main(String[] args) throws OpenDataException {
		memory();
		WeakHashMap<Integer, Integer> wHM = new WeakHashMap<Integer, Integer>();
		for(int i=0;i<5000;i++){
				wHM.put(i, new Integer(new Random().nextInt(11111)));
		}
		memory();
		System.out.println(wHM.size());
	}

	private static void memory() {
		// 可使用内存
		long totalMemory = Runtime.getRuntime().totalMemory() >> i;
		// 剩余内存
		long freeMemory = Runtime.getRuntime().freeMemory() >> i;
		// 最大可使用内存
		long maxMemory = Runtime.getRuntime().maxMemory() >> i;
		System.out.printf("可用内存:%dkb,剩余内存:%dkb,最大可用内存:%dkb \n", totalMemory, freeMemory, maxMemory);
	}

可用内存:1536kb,剩余内存:730kb,最大可用内存:1536kb 
可用内存:1536kb,剩余内存:337kb,最大可用内存:1536kb 
2656

软引用

类似弱引用,不过是他是在当内存不足时,才去回收,所以比弱引用的生存环境好一些

虚引用

与弱引用类似,只不过他是通过PhantomRefernce创建,对象被回收后放到ReferenceQueue中,等待你的进一步操作
比finalize更灵活
public static void main(String[] args) throws OpenDataException {
		Integer z=new Integer(1234);
		ReferenceQueue<Integer> r=new ReferenceQueue<>();
		PhantomReference<Integer> p=new PhantomReference<Integer>(z, r);
		System.out.println(p.get());
		System.out.println(r.poll());
		z=null;
		System.gc();
		System.out.println(p.get());
		System.out.println(r.poll());
	}

Reference

说到引用不得不说Reference,它是3种引用的抽象基类
PhantomReference 虚引用
SoftReference 软引用
WeakReference 弱引用
看一个例子一目了然
	static int i = 10;
	static Integer z = null;
	static ReferenceQueue<Integer> queue = null;

	public static void main(String[] args) throws OpenDataException {
                //软引用,当内存不足的时候才会销毁它gc
		ini();
		core(new SoftReference<Integer>(z, queue), "软引用");
                //弱引用扫到就销毁
		ini();
		core(new WeakReference<Integer>(z, queue), "弱引用");
                //扫到就销毁,虚引用的值是get不到的,只有在销毁后从队列中得到
		ini();
		core(new PhantomReference<Integer>(z, queue), "虚引用");
	}

	private static void core(Reference<Integer> rf, String name) {
		System.out.println("[" + name + " 取值] : " + rf.get());
		System.out.println("[队列取值] : " + queue.poll());
		z = null;
		System.gc();
		System.out.println("[值设置为空][调用GC]");
		System.out.println("[" + name + " 取值] : " + rf.get());
		System.out.println("[队列取值] : " + queue.poll());
		System.out.println("=====================");
	}

	private static void ini() {
		z = new Integer(999);
		queue = new ReferenceQueue<>();
	}

	 
Console
[软引用 取值] : 999
[队列取值] : null
[值设置为空][调用GC]
[软引用 取值] : 999
[队列取值] : null
=====================
[弱引用 取值] : 999
[队列取值] : null
[值设置为空][调用GC]
[弱引用 取值] : null
[队列取值] : java.lang.ref.WeakReference@15db9742
=====================
[虚引用 取值] : null
[队列取值] : null
[值设置为空][调用GC]
[虚引用 取值] : null
[队列取值] : java.lang.ref.PhantomReference@6d06d69c
=====================

WeakHashMap


定义

K是一个可以为空的弱键(弱引用)
先看下面代码:HashMap在极限内存情况下会抛出OOM
 
static int i = 10;
	public static void main(String[] args) throws OpenDataException {
		memory();
		HashMap<Integer, Integer> wHM = new  HashMap<Integer, Integer>();
		for (int i = 0; i < 50000; i++) {
			wHM.put(i, new Integer(new Random().nextInt(11111)));
		} 
		memory();
		System.out.println(wHM.size());
	}

	private static void memory() {
		// 可使用内存
		long totalMemory = Runtime.getRuntime().totalMemory() >> i;
		// 剩余内存
		long freeMemory = Runtime.getRuntime().freeMemory() >> i;
		// 最大可使用内存
		long maxMemory = Runtime.getRuntime().maxMemory() >> i;
		System.out.printf("可用内存:%dkb,剩余内存:%dkb,最大可用内存:%dkb \n", totalMemory, freeMemory, maxMemory);
	}

可用内存:1536kb,剩余内存:730kb,最大可用内存:1536kb 
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
然而我们改成WeakHashMap就不会有OOM,但是输出的size并不是我们想要的5000,
正如上面所说的弱引用,因为内存吃紧JVM开始进行gc,扫到了弱引用自然就要消灭他们。

源码


put
public V put(K key, V value) {
        Object k = maskNull(key); //maskNull ==> (key == null) ? new Object(): key;
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);
        //indexFor返回了h在tab中的hashCode下面看是否是存在的hashCode来进行替换
        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }
        //长度增加
        modCount++;
        Entry<K,V> e = tab[i];
        //根据i放入新值
        tab[i] = new Entry<>(k, value, queue, h, e);//注意这里的queue下面的Entry来解释
        if (++size >= threshold)//是否需要resize调整容器
            resize(tab.length * 2);
        return null;
    }

Entry
 private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
 //继承了弱引用,所以WeakHashMap保存的值都是WeakReference的派生类,当然他实现了Map.Entry
        V value;
        final int hash;
        Entry<K,V> next;

        /**
         * Creates new entry.
         */
        Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            //这里调用了父级 WeakReference的构造,这个构造正是上面我们讲引用时候所用到的 
            //传递一个ReferenceQueue传递一个引用队列,当值被回收队列来维持这个值 
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }

        @SuppressWarnings("unchecked")
        public K getKey() {
            return (K) WeakHashMap.unmaskNull(get()); 
           //指向了基类中的 T k,unmaskNull三目运算如果k是一个new Object()那么返回一个null
        }

        public V getValue() {
            return value;
        }

        public V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            K k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                V v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public int hashCode() {
            K k = getKey();
            V v = getValue();
            return Objects.hashCode(k) ^ Objects.hashCode(v);
        }

        public String toString() {
            return getKey() + "=" + getValue();
        }
    }

expungeStaleEntries
在很多操作的时候都包含了这个方法,我们来看看这个描述为“删除表中旧数据”的方法到底做了什么
 private void expungeStaleEntries() { //进行一次比对把引用队列中的垃圾数据从map中清理  
        for (Object x; (x = queue.poll()) != null; ) {
            //queue是引用队列 queue.poll从队列底部开始遍历 
            //确保引用队列是线程安全的
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x;
                int i = indexFor(e.hash, table.length);              
                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                while (p != null) {
                    Entry<K,V> next = p.next;
                    if (p == e) {//队列数据与table数据匹配
                        if (prev == e)
                            table[i] = next; //把当前值挤出table直接用next代替
                        else
                            prev.next = next; //改变p上一个地址值的next指针指向的内容                    
                        e.value = null; // =null帮助GC回收
                        size--; //容器大小变化
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }
之后无论是resize/remove操作都会调用expungeStaleEntries()来确保已被回收的数据,在map中被清理

结语

浅读map及其实现类就到这里了
因为是浅读,所以很多地方写的比较浅,还是需要大家去自己深入了解,ConcurrentHashMap 源码还要继续啃 偷笑






 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值