• Java中的软(弱)引用


    一、Java中的强、软、弱、虚引用

    在JDK中我们能够看到有一个java.lang.ref的包。这个包中就是Java中实现强、软、弱、虚引用的包,例如以下:


    PhantomReference

    虚引用:假设一个对象持有虚引用,就和没有持有引用一样,在不论什么时候都可能被垃圾回收器回收。

    虚引用主要用来跟踪对象被垃圾回收的活动,虚引用另一个和弱、软引用不同的地方是虚引用必须和引用队列联合使用。当垃圾回收器准备回收一个对象时。假设发现它还有虚引用,就会在回收对象内存之前,把这个虚引用加入到与之关联的引用队列中。

    程序能够通过推断引用队列中是否已经加入了虚引用,来知道对象是否将要被回收,这样我们就能够在对象被回收之前进行一些操作。

    Reference

    引用基类:这是一个抽象类。封装了关于引用的相关操作。如去除引用、比較、得到引用对象、推断引用地址是否相等。

    ReferenceQueue

    引用队列:在垃圾回收器回收对象之前,会将该引用加入到该队列。

    SoftReference

    软引用:假设内存空间不够用时,垃圾回收器就会回收该引用所引用到的内存对象。使用软引用能够实现内存敏感的快速缓存,软引用能够和引用队列联合使用。

    WeakReference

    弱引用:当垃圾回收器发现某个对象仅仅有弱引用时不管是否内存够用都会回收该对象,只是。因为垃圾回收器是一个优先级非常低的线程,因此不一定会非常快发现仅仅持有弱引用的对象。弱引用也能够和引用队列联合使用。

    二、一个软(弱)引用的样例

    以下我们来使用软引用读取一张图片,读出图片的宽和高,代码例如以下:

    package 软引用;
    
    import java.lang.ref.Reference;
    
    /**
     * 封装引用对象的基类
     * @author CodeingSnail
     *
     * @param <T>
     */
    public abstract class ReferenceObject<T>{
    	
    	public Reference<T> ref;
    	
    	protected abstract T getInstance();
    	protected abstract Reference<T> getReference(T t);
    	
    	/*
    	 * 获取引用对象及弱引用
    	 */
    	private T getRefrenceAndInstance(){
    		T t = getInstance();
    		getReference(t);
    		return t;
    	}
    	
    	/**
    	 * 获取被引用的对象
    	 * @return
    	 */
    	public T get(){
    		if(ref == null){
    			return getRefrenceAndInstance();
    		}
    		T t = ref.get();
    		if(t == null){
    			return getRefrenceAndInstance();
    		}
    		return t;
    	}
    	
    	/**
    	 * 清空对象的引用。回收对象
    	 */
    	public void recycle() {
    		if(ref != null){
    			ref.clear();
    			ref = null;
    		}
    	}
    }
    
    import java.lang.ref.Reference;
    import java.lang.ref.SoftReference;
    
    import javax.swing.ImageIcon;
    
    
    /**
     * 引用图片资源的类
     * @author Administrator
     *
     */
    public class ReferenceBitmap extends ReferenceObject<ImageIcon>{
    	String url;
    	public ReferenceBitmap(String url) {
    		this.url = url;
    	}
    
    	@Override
    	protected Reference<ImageIcon> getReference(ImageIcon imageIcon) {
    		return new SoftReference<ImageIcon>(imageIcon);
    	}
    
    	@Override
    	protected ImageIcon getInstance() {
    		return new ImageIcon(url);
    	}
    	
    }
    package 软引用;
    
    public class Client {
    	
    	private ReferenceBitmap referenceBitmap;
    	
    	public static void main(String[] args) {
    		Client client = new Client();
    		client.referenceBitmap = new ReferenceBitmap("E:\test.png");
    		System.out.println("图片的高度是:" + client.referenceBitmap.get().getIconHeight());
    		System.out.println("图片的宽度是:" + client.referenceBitmap.get().getIconWidth());
    	}
    }
    
    在上面样例中我们先定义了一个引用的抽象类,而且给子类提供两个回调方法用来创建图片对象和所须要的软引用(也能够是弱引用),在get()方法中获取对象的实例。事实上上面的样例是一个通用的方法。我们能够在getReference中定义我们须要的引用类型。假如我们如今已经非常明白,我们要的就是软引用,能够将代码简化例如以下:

    public class Client {
    	SoftReference<ImageIcon> softRefrence;
    	private ReferenceBitmap referenceBitmap;
    	
    	public static void main(String[] args) {
    		Client client = new Client();
    		client.referenceBitmap = new ReferenceBitmap("E:\test.png");
    		System.out.println("图片的高度是:" + client.referenceBitmap.get().getIconHeight());
    		System.out.println("图片的宽度是:" + client.referenceBitmap.get().getIconWidth());
    		
    		//简化方法
    		client.softRefrence = 
    				new SoftReference<ImageIcon>(new ImageIcon("E:\test.png"));
    		System.out.println("图片的高度是:" + client.softRefrence.get().getIconHeight());
    		System.out.println("图片的宽度是:" + client.softRefrence.get().getIconWidth());
    	}
    }

    三、怎样配合引用队列使用

    我们以下来分析一下java.util包下的WeakHashMap类,打开JDK后会发现对这个类有一个非常长的描写叙述。我们来一起看一下大概意思。
    Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.
    这句话的大概意思是WeakHashMap的哈希表是基于Map接口的。当中key中保存的是value的一个弱引用。当系统回收了该key所相应的实际对象之后。WeakHashMap会自己主动删除该key相应的key-value对。
        /**
         * The entries in this hash table extend WeakReference, using its main ref
         * field as the key.
         */
        private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
            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);
                this.value = value;
                this.hash  = hash;
                this.next  = next;
            }
    
            @SuppressWarnings("unchecked")
            public K getKey() {
                return (K) WeakHashMap.unmaskNull(get());
            }
    
            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(); } }

    WeakHashMap.Entry继承自WeakReference,在构造方法中能够看到直接将key交给WeakReference并通过ReferenceQueue关联。

        /**
         * Expunges stale entries from the table.
         */
        private void expungeStaleEntries() {
            for (Object x; (x = queue.poll()) != null; ) {
                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) {
                            if (prev == e)
                                table[i] = next;
                            else
                                prev.next = next;
                            // Must not null out e.next;
                            // stale entries may be in use by a HashIterator
                            e.value = null; // Help GC
                            size--;
                            break;
                        }
                        prev = p;
                        p = next;
                    }
                }
            }
        }
    WeakHashMap中有一个私有的expungeStaleEntries方法。会在大部分共同拥有方法中被调用,这种方法会将ReferenceQueue中全部失效的引用从Map中移除。WeakHashMap不会自己主动释放失效的引用,仅当包括了expungeStaleEntries方法被调用的时候才会释放。以下一个小样例来看一下WeakHashMap的使用:
    	public static void main(String[] args) {
    		WeakHashMap<String, String> map = new WeakHashMap<String, String>();
    		map.put(new String("1"), "1");
    		map.put("2", "2");
    		String s = new String("3");
    		map.put(s, "3");
    		int i = 0;
    		while(map.size() > 0){
    			try {
    				Thread.sleep(500);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println("Map Size: " + map.size());
    			System.out.println(map.get("1"));
    			System.out.println(map.get("2"));
    			System.out.println(map.get("3"));
    			if(i == 3) s = null;
    			System.gc();
    			i++;
    		}
    	}
    输出结果:
    Map Size: 3
    1
    2
    3
    Map Size: 2
    null
    2
    3
    Map Size: 2
    null
    2
    3
    Map Size: 2
    null
    2
    3
    Map Size: 1
    null
    2
    null
    Map Size: 1
    null
    上面的样例中。第一个key外部没有强引用,则仅仅打印了一次就被回收器回收,第三个key有外部的强引用。当我们将外部引用去掉后也被垃圾回收器回收。第二个key是被字符串常量池所引用,所以一直存在。


  • 相关阅读:
    【笔记】 寻址方式
    今日思考之 20200614:java 中 null 是否对 gc 有帮助?
    分布式唯一ID生成方案对比分析 笔记
    请不要再称数据库是CP或者AP——CAP的误导(短板)和它的使命
    延迟初始化中的 双重检查模式 和 延迟占位类模式 你都用对了吗?
    redis bitmap
    RabbitMQ 使用 Policies HTTP API 绑定和解绑 DLX
    spring boot 自动装配的实现原理和骚操作,不同版本实现细节,debug 到裂开......
    netty 学习笔记三:大跃进,使用 netty 实现 IM 即时通讯系统
    一道 Java 方法传值面试题——Java方法传值的值传递概念和效果 + Integer 缓存机制 + 反射修改 private final 域
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6759153.html
Copyright © 2020-2023  润新知