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是被字符串常量池所引用,所以一直存在。

时间: 2024-10-13 02:08:06

Java中的软(弱)引用的相关文章

JAVA中的四种引用以及ReferenceQueue和WeakHashMap的使用示例

简介: 本文主要介绍JAVA中的四种引用: StrongReference(强引用).SoftReferenc(软引用).WeakReferenc(弱引用).PhantomReference(虚引用)的作用.同时我们还将介绍ReferenceQueue和WeakHashMap的功能和使用示例. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. JAVA中的四种引用 四种引用中,软引用.若引用.虚引用都需要相关类来创建.创建的时候

浅谈Java中的对象和引用

浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是"对象和对象引用",很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起了解一下对象和对象引用之间的区别和联系. 1.何谓对象? 在Java中有一句比较流行的话,叫做"万物皆对象",这是Java语言设计之初的理念之一.要理解什么是对象,需要跟类一起结合起来理解.下面这段话引自<Java编程思想>中的一段原话: "按照通

java 中值传递和引用传递(转)

java中给函数传递参数的方式有两种:值传递和引用传递.一般而言,基本类型是值传递:引用类型是引用传递.但传值时到达发生了什么? 1.基本类型 8个基本类型(byte,short,int,long,float,double,char,boolean)是值传递. 1 public class ValueTest { 2 3 public static void main(String[] args) { 4 int a = 10; 5 changeVal(a); 6 System.out.prin

JAVA中MESSAGEBOX,静态类直接引用

原文:JAVA中MESSAGEBOX,静态类直接引用 package cisdi.mes.wrm.mcode.serviceImpl; import javax.persistence.Entity; import javax.swing.JOptionPane; @Entity public class McodeTest { /** * @param <WrmMtlUomClassesTlService> * @param args */ public static void main(S

Java中的四种引用(强引用、软引用、弱引用、虚引用)

以下内容摘自<深入理解Java虚拟机 JVM高级特性与最佳实践>第2版,强烈推荐没有看过的同学阅读,读完的感觉就是"原来学的都是些什么瘠薄东西(╯‵□′)╯︵┴─┴" 在JDK1.2以前,Java中的引用的定义很传统:如果 reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用.这种定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些"食之无味,弃之可惜"的对象就显得

Java 中的四种引用

1.强引用(Strong Reference)在 Java 中四种引用中是"最强"的,我们平时通过 new 关键字创建的对象都属于强引用,如下面的代码: Person person = new Person();其中的 person 就是一个强引用,只有当它不再被使用后才会被垃圾回收器回收掉.当内存不足时,但是其依然在被使用中,那么垃圾回收器也不会回收其引用的对象:JVM 宁愿报"内存泄漏错误 (OutofMemoryError)",终止程序也不会回收此引用所关联的

Java 中的四种引用及垃圾回收策略

Java 中有四种引用:强引用.软引用.弱引用.虚引用: 其主要区别在于垃圾回收时是否进行回收: 1.强引用 使用最普遍的引用.如果一个对象具有强引用,那就 类似于必不可少的生活用品,垃圾回收器绝不会回收它.当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题. 2.软引用(SoftReference) 如果一个对象只具有软引用,那就类似于可有可物的生活用品.如果内存空间足够,垃圾回收器就不会回收它,如果

java中的四种引用

Java 中有四种引用:强引用.软引用.弱引用.虚引用: 其主要区别在于垃圾回收时是否进行回收: 1.强引用 使用最普遍的引用.如果一个对象具有强引用,那就 类似于必不可少的生活用品,垃圾回收器绝不会回收它.当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题. 2.软引用(SoftReference) 如果一个对象只具有软引用,那就类似于可有可物的生活用品.如果内存空间足够,垃圾回收器就不会回收它,如果

Java中的四种引用及其应用场景是什么

1.强引用: 通常我们使用new操作符创建一个对象时所返回的引用即为强引用.强引用即使当前的内存不足也不会被回收而是由JVM抛出OutOfMemoryError 错误.如果想要中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象. String str = "hello"; // 强引用 str = null; // 取消强引用 2.软引用 内存空间足够的时候则正常使用,内存空间不足的时候则会被垃圾回收器回收.可用于图片缓存中,