1、强引用
只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OOM错误也不会回收这种对象
String str = "hello world"
Object obj = new Object
这种都是强引用,这种引用保存在栈中,堆存放的是引用内容。
当方法运行完后退出方法栈,这种引用不存在,对象才会回收。对于全局变量来说,obj=null,才会触发GC回收。
显式地设置obj为null,或超出对象的生命周期范围,则gc认为该对象不存在引用才会回收。
2、软引用
在内存足够的情况下直接通过使用软引用取值,无需重新加载数据;一个对象只有软引用可达时,内存不足是可以被回收的。基于这种设置,一般可用作缓存。
Browser prev = new Browser(); // 获取页面进行浏览
SoftReference sr = new SoftReference(prev); // 浏览完毕后置为软引用
if(sr.get()!=null){
rev = (Browser) sr.get(); // 还没有被回收器回收,直接获取
}else{
prev = new Browser(); // 由于内存吃紧,所以对软引用的对象回收了
sr = new SoftReference(prev); // 重新构建
}
GC如何判断是否可以回收了呢?
google大神解疑:http://jeremymanson.blogspot.com/2009/07/how-hotspot-decides-to-clear_07.html
3、弱引用
与软引用区别在于拥有更短的生命周期,一个对象只有弱引用可达时,无论内存是否充足,GC时都会回收它的内存。
ThreadLocal中ThreadLocalMap.Entry就是弱引用。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中
public class Test {
private static ReferenceQueue<Ref> queue = new ReferenceQueue<>();
public static void main(String[] args) throws InterruptedException {
LinkedList<WeakReference<Ref>> weakList = new LinkedList<WeakReference<Ref>>();
for (int i = 0; i < 3; i++) {
WeakRef weakRef = new WeakRef(new Ref("Weak " + i), queue);
weakList.add(weakRef);
System.out.println(weakRef.get());
}
System.gc();
Thread.currentThread().sleep(6000);
Reference<? extends Ref> queueRef = null;
while ((queueRef = queue.poll()) != null) {
System.out.println("queue: " + ((WeakRef) queueRef).weakId);
}
}
}
class Ref {
String id;
// 占用空间,让线程进行回收
byte[] b = new byte[2 * 1024];
public Ref(String id) {
this.id = id;
}
@Override
public String toString() {
return "ref:" + id;
}
}
class WeakRef extends WeakReference<Ref> {
String weakId;
public WeakRef(Ref ref, ReferenceQueue<? super Ref> q) {
super(ref, q);
this.weakId = ref.id;
}
}
ref:Weak 0
ref:Weak 1
ref:Weak 2
queue: Weak 2
queue: Weak 0
queue: Weak 1
4、虚引用
如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello world"), queue);
System.out.println("before gc: " + pr.isEnqueued());
System.gc();
Thread.sleep(1000);
System.out.println("after gc: " + pr.isEnqueued());
}
//
before gc: false
after gc: true
虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
pr.get()直接返回null,