强、软、弱、虚引用例子

下面用更多的例子来说明一下各种引用的用法:

第一个场景:后退

public class LastStep {
	Holder d = new Holder();
	SoftReference<Holder> sr = new SoftReference<Holder>(d);
	public static void main(String[] args) {
		
	}
	
	public Holder getHolder(){
		if(sr.get() == null){
			return new Holder();
		}else{
			return sr.get();
		}
	}
}
当在比较短的时间内想再次获取已经用过的对象的时候可以这样来做, 如果还在就直接使用,不在了才创建,可以减少new 的对象的次数

第二个场景:缓存

很多时候我们可以使用一个Map作为缓存,这样的问题在于保存在map中的对象不会被垃圾回收掉。 
借助于SoftReference对象的能够通过get()找回还没被回收对象的能力,可以把原本要缓存的对象改为缓存reference,这样,对象如果在内存足够的时候会被缓存,而不够的时候又会清空掉。
但是又会引入一个问题,SoftReference自己的对象又会无法清理,这里可以借助ReferenceQueue的能力,当Reference所持有的对象被GC后会把该Reference放到queue中的能力。通过遍历queue的方式来找到那个SoftReference已经不需要存在了来进行清理。
基于上诉内容,做一个对内存比较敏感的缓存,代码如下:
用于占位引起GC的Holder:
public class Holder {
	private static final int MB = 1024 * 1024;
	
	private byte[] holder;
	public Holder() {
		this.holder = new byte[1 * MB];
	}
}


class ObjectRef<T> extends SoftReference<T>{
	private String key;
	public ObjectRef(String key, T value, ReferenceQueue<T> q) {
		super(value);
		this.setKey(key);
	}
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
}


public class Cache<T> {
	private Map<String, ObjectRef<T>> cacheData;
	private ReferenceQueue<T> q;
	
	public static <T> Cache<T> getInstance(){
		return new Cache<T>();
	}
	
	private Cache() {
		cacheData = new HashMap<String, ObjectRef<T>>();
		q = new ReferenceQueue<T>();
	}
	
	public T getData(String key){
		T t = null;
		if(cacheData.containsKey(key)){
			ObjectRef<T> sr = cacheData.get(key);
			t = sr.get();
		}
		return t;
	}
	
	public void cache(String key, T t) {
		cleanCache();
		ObjectRef<T> ref = new ObjectRef<T>(key, t, q);
		cacheData.put(key, ref);
	}

	private void cleanCache() {
		ObjectRef<T> ref = null;
		while((ref = (ObjectRef<T>) q.poll()) != null){
			cacheData.remove(ref.getKey());
		}
	}

	public static void main(String[] args) throws InterruptedException {
		Cache<Holder> c = Cache.getInstance();
		{
			Holder d1 = new Holder();
			c.cache("test", d1);
			Holder holder = c.getData("test");
			System.out.println(holder);
		}
		
		Holder d2 = new Holder();
		Holder d3 = new Holder();
		Holder d4 = new Holder();
		Holder d5 = new Holder();
		Holder holder = c.getData("test");
		System.out.println(holder);
	}
}
使用JVM参数执行:
-verbose:gc -Xms2m -Xmx2m -Xmn1m -XX:SurvivorRatio=8 -XX:+PrintGCDetails
该代码将会打印:
com.price.effective.create.Holder@ca0b6
null
说明其在内存足够的时候不会被回收,不够的时候才会被回收

场景三 使用WeakHashMap 

对于上面的例子, 及一些用全局Map保存了长连接等大对象的场景, 合适回收这些对象的问题可以很好的使用WeakHashMap来解决。
WeakHashMap的key是一些WeakReference, 当Key失效时,其会清理该key,及该key对应的值。这样可以防止内存泄露。

场景四  检测对象被回收

虽然有finallize方法会在垃圾回收的时候执行,但是这样我们并不能很好的记录那些文件被回收了,使用虚引用,可以在不影响对象声明周期的情况下,监控对象的被回收的情况。如下:
public class ReferenceLeaning {
	public static void main(String[] args) {
		{
			final ReferenceQueue<Holder> q = new ReferenceQueue<Holder>();
			Holder d1 = new Holder();
			PhantomReference<Holder> r1 = new PhantomReference<Holder>(d1, q);
			
			new Thread(new Runnable() {
				@Override
				public void run() {
					while(true){
						PhantomReference<Holder> ref = (PhantomReference<Holder>) q.poll();
						if(ref != null){
							try {
								Field referent = Reference.class  
								        .getDeclaredField("referent");
								referent.setAccessible(true);  
	                            Object result = referent.get(ref);
	                            System.out.println(result);
	                            result = null;
							} catch (SecurityException e) {
								e.printStackTrace();
							} catch (NoSuchFieldException e) {
								e.printStackTrace();
							} catch (IllegalArgumentException e) {
								e.printStackTrace();
							} catch (IllegalAccessException e) {
								e.printStackTrace();
							}  
                            
						}
					}
				}
			}).start();
		}
		Holder d2 = new Holder();
		Holder d3 = new Holder();
		Holder d4 = new Holder();
	}
}

可以打印出正在准备回收的类,
因为PhantomReference的get方法永远返回null,因此用反射来得到其值, 但是例子中的这段代码又会使得对象不被回收了,因此最后又赋值为null.
其实这个例子并不好。 也没想到更有用的使用虚引用的方式,如果以后遇到了再补充。




<think>好的,我现在需要回答用户关于Java中四种引用类型(引用引用引用引用)的区别和应用场景的问题。首先,我得回忆一下这些引用的基本概念,然后整理它们的区别和实际应用。 首先,引用是最常见的,比如`Object obj = new Object();`这样的引用,只要引用存在,对象就不会被回收。用户提供的引用资料里提到引用是内存泄漏的主要原因之一,这一点需要调。 然后是引用(SoftReference)。我记得引用关联的对象在内存不足时会被回收,适合用来做缓存。比如图片缓存,应用在需要时可以从内存中获取,内存不够时系统自动清理。引用[1]和[2]都提到引用在内存紧张时会被回收,应该用这个特性举例说明应用场景。 接下来是引用(WeakReference)。根据引用[3],引用在下一次垃圾回收时就会被回收,不管内存是否足够。典型的应用是WeakHashMap,或者某些临时性的数据存储,比如监听器,当外部引用消失后,引用不会阻止回收。比如Android中的Context引用,避免内存泄漏。 引用(PhantomReference)比较特殊,无法通过引用访问对象,必须和ReferenceQueue一起使用。主要用于跟踪对象被回收的状态,比如在对象被回收时做资源清理工作。比如NIO中的DirectByteBuffer可能会用到引用来释放堆外内存。 现在需要把这些点组织起来,先分别介绍每种引用,然后对比它们的区别,再讲应用场景。需要注意用户提供的引用材料中的内容,适当引用。例如,引用[3]提到引用在垃圾回收时一定会被回收,而引用在内存不足时才回收,这是关键区别。 可能用户还会混淆这些引用类型的实际用法,所以需要举一些代码例子。比如引用如何创建,引用引用如何用对应的类包装,引用需要结合引用队列使用等。 然后需要回答用户的问题,区分这四种引用的区别和应用场景。注意使用用户提供的引用标记[^1]、[^2]、[^3],确保正确对应到参考资料里的内容。 最后,生成相关问题。可能包括:各引用类型在垃圾回收中的表现,如何防止内存泄漏,实际开发中的应用案例等。需要确保问题与主题相关,帮助用户进一步理解。 在组织语言时,要保持结构清晰,分点说明,使用中文,并确保LaTeX格式正确,比如变量用$...$包裹。例如,在说明引用时,可以提到`obj`这个引用指向的对象不会被回收,除非`obj = null`。 总结步骤: 1. 分别介绍四种引用的定义和特性。 2. 对比它们的区别,特别是回收条件和度。 3. 应用场景举例,结合代码示例。 4. 引用用户提供的参考资料中的要点,添加引用标记。 5. 生成相关问题,供用户深入思考。 检查是否有遗漏,比如引用的具体使用例子是否正确,是否有混淆引用引用的地方。确保每个引用类型都有对应的应用场景,并且正确引用资料中的内容。</think>### Java中四种引用的区别与应用场景 #### 1. 引用(Strong Reference) - **定义**:最常见的引用类型,通过`new`关键字创建的对象默认关联引用。例如: ```java Object obj = new Object(); // obj是引用 ``` - **特性**:只要引用存在,对象**不会被垃圾回收**,即使内存不足时JVM会抛出`OutOfMemoryError`也不会回收。 - **应用场景**:普通对象创建,如集合、业务逻辑类等。 - **风险**:引用未及时断开(如`obj = null`)可能导致内存泄漏。 --- #### 2. 引用(Soft Reference) - **定义**:通过`SoftReference`类包装对象,例如: ```java SoftReference<Bitmap> cache = new SoftReference<>(loadBitmap()); ``` - **特性**:当**内存不足**时,引用关联的对象会被回收。适合缓存敏感型数据[^2]。 - **应用场景**:图片缓存、临时计算结果缓存(如频繁查询的数据库结果)。 --- #### 3. 引用(Weak Reference) - **定义**:通过`WeakReference`类包装对象,例如: ```java WeakReference<EventListener> listenerRef = new WeakReference<>(listener); ``` - **特性**:**无论内存是否充足**,只要发生垃圾回收,引用对象就会被回收。 - **应用场景**: - `WeakHashMap`:键为引用,适合存储元数据(如线程上下文)。 - 避免内存泄漏(如Android中的`Handler`临时引用`Activity`)。 --- #### 4. 引用(Phantom Reference) - **定义**:通过`PhantomReference`类实现,必须与`ReferenceQueue`配合使用: ```java ReferenceQueue<File> queue = new ReferenceQueue<>(); PhantomReference<File> phantomRef = new PhantomReference<>(file, queue); ``` - **特性**: - 无法通过引用访问对象(`get()`始终返回`null`)。 - 对象被回收时,引用会加入关联的队列,用于**跟踪对象销毁状态**。 - **应用场景**: - 堆外内存管理(如NIO的`DirectByteBuffer`在回收时触发本地内存释放)。 - 资源清理(如关闭文件句柄)。 --- #### 区别对比 | 引用类型 | 回收条件 | 度 | 典型应用 | |----------|-------------------------|------|----------------------| | 引用 | 永不回收(除非显式置空)| 最高 | 普通对象 | | 引用 | 内存不足时回收 | 次高 | 缓存 | | 引用 | 垃圾回收时立即回收 | 较低 | 避免内存泄漏 | | 引用 | 对象回收后通知 | 最低 | 资源释放跟踪 | --- #### 代码示例 ```java // 引用 Object strongRef = new Object(); // 引用(缓存示例) SoftReference<CacheData> softRef = new SoftReference<>(loadCache()); // 引用(WeakHashMap示例) WeakHashMap<Key, Metadata> weakMap = new WeakHashMap<>(); // 引用(资源清理) ReferenceQueue<Resource> queue = new ReferenceQueue<>(); PhantomReference<Resource> phantomRef = new PhantomReference<>(resource, queue); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值