一、四大引用级别的概念
- 强引用:就是正常的引用,类似于下面:
-
Object object = new Object();
,object就是一个强引用,gc是不会清理一个强引用引用的对象的,即使面临内存溢出的情况。
- 软引用:SoftReference,GC会在内存不足的时候清理引用的对象:
SoftReference reference = new SoftReference(object); object = null;
- 弱引用:GC线程会直接清理弱引用对象,不管内存是否够用:
WeakReference reference = new WeakReference(object); object = null;
- 虚引用:和弱引用一样,会直接被GC清理,而且通过虚引用的get方法不会得到对象的引用,形同虚设,这里弱引用是可以的:
PhantomReference refernce = new PhantomReference(object); object = null;
二、四大引用级别之间的区别
强引用和软引用
- 这个比较简单,软引用只有在内存不足的时候才会被清理,而强引用什么时候都不会被清理(程序正常运行的情况下),即使是内存不足,利用这一个特性,可以做一些缓存的工作,下面的应用会讲到。
软引用和弱引用
- 弱引用不会影响GC的清理,也就是说当GC检测到一个对象存在弱引用也会直接标记为可清理对象,而软引用只有在内存告罄的时候才会被清理
弱引用和虚引用
- 说这个之前要说一下ReferenceQueue的概念,ReferenceQueue是一个队列,初始化Reference的时候可以作为构造函数的参数传进去,这样在该Reference的referent域(Reference用来保存引用对象的属性)指向的引用对象发生了可达性的变化时会将该Reference加入关联的队列中,这个具体的变化根据Reference的不同而不同。官方APi对ReferenceQueue的介绍:
Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected.
- 简单翻译就是:当reference object所引用的对象发生了适当的可达性变化时,GC向该队列追加该引用对象(reference object)。
- 弱引用和虚引用的区别就在于被加入队列的条件不同,这里主要侧重于考虑对象所属的类重写了finalize方法,将对象的状态归纳为三种:finalizable, finalized、reclaimed,分别代表:未执行finalize函数、已经执行finalize函数,已经回收。如果没有重写finalize函数的话下面再考虑。
- 虚引用必须和一个ReferenceQueue联合使用,当GC准备回收一个对象的时候,如果发现该对象还有一个虚引用,就会将这个虚引用加入到与之关联的队列.
- 弱引用:当GC第一次试图回收该引用指向的对象时会执行该对象的finalize方法,然后将该引用加入队列中,但是该引用指向的对象是可以在finlize函数中“复活”的,所以即使通过Reference的get方法得到的是null,而且reference被加入到了ReferenceQueue,这个对象仍然是存活的,这种现象是有点违背对象正常生命周期的,下面以代码示例:
package com;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class Main {
static Weak weak;
static class Weak {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize() is called");
weak = this;//复活对象
System.out.println("after finalize ,object is alive again");
}
}
public static void main(String[] args) throws InterruptedException {
weak = new Weak();
ReferenceQueue queue = new ReferenceQueue<>();
WeakReference<Weak> weakReference = new WeakReference<Weak>(weak, queue);
// PhantomReference<Weak> weakReference = new PhantomReference<>(weak,queue);
if (weakReference.get() == null) {
System.out.println("before gc : reference is not available");
} else {
System.out.println("before gc : reference is available");
}
weak = null;
System.gc();//执行GC线程
Thread.sleep(3000);
if (weakReference.get() == null) {
System.out.println("after gc : reference is not available");
} else {
System.out.println("after gc : reference is available");
}
if (queue.poll() == null) {
System.out.println("after gc : reference is not in queue");
} else {
System.out.println("after gc : reference is in queue");
}
weak=null;
System.gc();//再次执行GC
Thread.sleep(3000);
if (queue.poll() == null) {
System.out.println("gc agaain : reference is not in queue");
} else {
System.out.println("gc agaain : reference is in queue");
}
}
}
- output:
before gc : reference is available
finalize() is called
after finalize ,object is alive again
after gc : reference is not available
after gc : reference is in queue
gc agaain : reference is not in queue
- 可以看到,对象在复活之后,reference不可用,而且reference已经被加入到队列中。
- 虚引用:虚引用只有在对象处于reclaimed状态时才会将相关reference加入到队列,可以根据这个特性检测对象准确的生命周期,比如可以知道对象精确的销毁时间。
- 和上面相同的代码,输出如下:
before gc : reference is not available
finalize() is called
after finalize ,object is alive again
after gc : reference is not available
after gc : reference is not in queue
gc again : reference is in queue
- 我们可以根据输出看到明显的区别:
- 弱引用可以得到对象的引用,而虚引用不可以
- 弱引用在第一次被GC清理的时候会调用finalize方法,然后将reference加入到队列,即使这时候的对象是存货状态,而虚引用在第二次GC执行,对象处于reclaimed状态时才会将reference加入到队列。
- 顺便提一下软引用,在这个方面,软引用和弱引用是一样的,也就是说即使加入到了队列中,也不能确定对象是否销毁。
三、四大引用的应用
软引用
- 缓存敏感数据
- 适用情景:一个web应用,查询联系人数据,如果在浏览的过程中点击了返回上一条查询结果,正常处理就是再重新查询一次,然而这样是很浪费时间的,所以如果能将上一次的查询结果缓存下来就会极大的提升性能,所以可以将查询结果保存为软引用,这样在内存充足的情况下GC都不会释放掉这个引用指向的对象,下次需要结果的时候可以先判断一下这个对象是否被释放,如果没有就可以直接利用不用再次查询。
- 当软引用所指向的对象被回收的时候,通过get方法返回的是null,如果在构造软引用的时候传入了一个ReferenceQueue,就是将这个reference加入到队列,然后我们可以清理这些无用的reference。实际上我们最好这样做,否则会造成大量无用reference导致的内存泄漏。
- 下面举一个缓存的例子:
package com;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
public class Main {
}
class People{
String id;
String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class PeopleCache {
private static PeopleCache singlonCache;
private HashMap<String, PeoReference> refChache;//用来存储缓存的数据
private ReferenceQueue queue;
//自定义引用类,增加属性_key,方便在缓存数据中查找
static class PeoReference extends SoftReference {
private String _key;
public PeoReference(People referent, ReferenceQueue q) {
super(referent, q);
_key = referent.getId();
}
}
private PeopleCache(){
this.queue=new ReferenceQueue();
this.refChache=new HashMap<>();
}
public static PeopleCache getInstance(){
if(singlonCache==null){
singlonCache=new PeopleCache();
}
return singlonCache;
}
public void cachePeople(People people){
cleanCache();//清除已经标记为垃圾的引用
PeoReference reference = new PeoReference(people, queue);
refChache.put(people.getId(), reference);//将对象的软引用保存到缓存中
}
public void cleanCache(){
PeoReference reference = null;
while ((reference = (PeoReference)queue.poll())!=null){
refChache.remove(reference._key);
}
}
public People getCachedPeople(String key){
People people = null;
if (refChache.containsKey(key)){
people= (People) refChache.get(key).get();
System.out.println("get object from cache");
}else{
people = new People();
System.out.println("get object from database or web server");
}
return people;
}
}
- 当需要查询数据的时候先获取一个PeopleCache实例,然后使用getCachedPeople方法试图去获取指定ID的数据,如果缓存中有的话就从缓存中获取,没有的话就正常查询获取,并且将查询结果缓存到HashMap。
弱引用
- WeakHashMap
- 具体使用和HashMap一样,但是它的键存放的是对象的弱引用,如果该弱引用指向的对象被垃圾回收了,WeakHashMap就会删除对应的数据(键值对)。
- 用来缓存那些非必需存在的数据。
虚引用
网友评论