Java中存在四种引用,StrongReference(强引用) 、SoftReferenc(软引用) 、WeakReferenc(弱引用)、PhantomReference(虚引用).虽然不常用,但是对于理解Java的回收等级还是很有帮助的,一句话来说这些引用只是不同回收等级的一种表现形式.
StrongReference(强引用)
强引用是最经常使用的一种引用,如new操作创建的对象就属于强引用.如下代码,对于强引用要记住无论如何JVM都不会去回收其内存.
Object obj = new Object();
SoftReferenc(软引用)
软引用是由java.lang.ref.SoftReference所提供的功能,被其所关联的对象不存在强引用并且此时JVM内存不足才会去回收该对象.
个人不知道其用处,做缓存的话,现在的企业项目基本不是单体架构所以用处不大,倒是可以做内存警告,当对象被回收时则说明系统所需要的内存不足,那么就可以发邮件通知相关人员.
WeakReferenc(弱引用)
弱引用是java.lang.ref包下的WeakReferenc类所提供的包装功能,对于弱引用JVM会回收仅被弱引用所关联的对象.也就是说弱引用对象会在一次gc之后被回收,如下代码,其中obj1没被回收,因为其的引用是强引用,但是weakObj1与其关联是弱引用,因此不属于被收回对象.weakObj2所关联的new Object()只有一个弱引用关联,因此会被回收.
Object obj1 = new Object();
WeakReference weakObj1 = new WeakReference(obj1);
WeakReference weakObj2 = new WeakReference(newObject());//主动回收
System.gc();
System.out.println(weakObj1.get()); // 非
nullSystem.out.println(weakObj2.get()); // null
Java中提供了一个很棒的工具类WeakHashMap,按照注释所说,该类是一个键为弱引用类型的Map,与传统Map不同的是其键会自动删除释放掉,因为gc()时会自动释放,因此很适合做缓存这一类的需求,下面代码是Tomcat所实现的LRU(最少使用策略)缓存算法的实现,关键点在注释中给出.
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;public final class ConcurrentCache {
//LRU所允许的最大缓存量
private final int size;
private final Map eden;
private final Map longterm;
public ConcurrentCache(int size)
{
this.size = size;
//eden是主要缓存
this.eden = new ConcurrentHashMap<>(size);
//longterm是实现LRU算法的关键点.
this.longterm = new WeakHashMap<>(size);
} //get是先从eden中取出缓存,当不存在时则去longterm中获取缓存,并且此时获取到的缓存说明还在使用,因此会put到eden中(LRU算法)
public V get(K k) { V v = this.eden.get(k); if (v == null)
{ synchronized (longterm) { v = this.longterm.get(k); }
if (v != null) { this.eden.put(k, v);
}
}
return v;
} //put操作当size大于LRU最大容量时,则把缓存都放入到longterm,当this.eden.clear()后使其成为弱引用,那么LRU的实现则在get方法中体现了出来.
public void put(K k, V v)
{ if (this.eden.size() >= size)
{ synchronized (longterm)
{ this.longterm.putAll(this.eden); } this.eden.clear();
} this.eden.put(k, v);
}}
此方法如果操作时刚好遇到了一次gc,那么longterm的引用就会丢失,那么缓存就gg了.
PhantomReference(虚引用)
虚引用是由java.lang.ref.PhantomReference所提供的关联功能,虚引用对其原对象的生命周期毫无影响,其可以算是一种标记,当其所引用对象被回收时其会自动加入到引用队列中.也就是说你可以通过虚引用得到哪些对象已被回收.具体用法可以分析common.io中的org.apache.commons.io.FileCleaningTracker
该类中有一内部类class Tracker extends PhantomReference,也就是其包裹着虚引用对象,分析其构造函数,marker参数是该具体的虚引用,当marker被回收时,该对应的Track会被加入到引用队列queue中.
Tracker(String path, FileDeleteStrategy deleteStrategy, Object marker, ReferenceQueue<? super Object> queue) { //marker是具体的虚引用对象 super(marker, queue); this.path = path; this.deleteStrategy = deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy;}
文件删除则是该类维护的一个线程来进行的操作,既然对象回收后会加入到引用队列queue,那么该线程要做的功能自然是从引用队列中获取到对应的Track,然后执行其删除策略.
在这个流程中虚引用起到的是跟踪所包裹对象作用,当包裹的的对象被回收时,这边会得到一个通知(将其加入到引用队列).
@Override public void run()
{
// thread exits when exitWhenFinished is true and there are no more tracked objects while (exitWhenFinished == false || trackers.size() > 0) { try { // Wait for a tracker to remove. Tracker tracker = (Tracker) q.remove(); // cannot return null trackers.remove(tracker); if (!tracker.delete()) { deleteFailures.add(tracker.getPath()); } tracker.clear(); } catch (InterruptedException e) { continue; } } }
注:加群要求 学习交流群:450936584
1、想学习JAVA这一门技术, 对JAVA感兴趣,想从事JAVA工作的。
2、工作0-5年,感觉自己技术不行,想提升的
3、如果没有工作经验,但基础非常扎实,想提升自己技术的。
4、还有就是想一起交流学习的。
5、小号加群一律不给过,谢谢。
群内每天会分享最新的视频和资料,可以免费领取学习视频和资料
转发此文章请带上原文链接,否则将追究法律责任!