为啥要引进四种引用?
jdk1.2之前的引用定义:如果reference类型的数据中存储的数值代表另一块内存的起始地址,就称这块内存代表着一个引用。
上述弊端:一个对象只能表示两种状态,即被引用,未被引用。
如果我们希望描述这样一类对象:当内存空间足够时则保留在内存中,如果内存空间在进行垃圾收集后还是比较紧张则可以抛弃这些对象。于是jdk1.2之后扩展了引用。
引用分类
1、强引用(StrongReference)
强引用就是指在程序代码中普遍存在的,类似Object o = new Object()这类的引用。如果一个对象持有强引用,那么垃圾回收器绝不会回收他,当内存空间不足时,jvm宁愿抛出OutOfMemoryError使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
ps:如果强引用不在使用时可以赋值为null,这样垃圾回收器则认为对象未被引用,在合适的时候便会进行回收。
2、软引用(SoftReference)
- 作用:用来描述一些有用但并非重要的对象。
- 特点:
1、系统内存空间充足时,垃圾回收器不会回收它。只要垃圾回收器没有回收它,该对象就可以被程序使用。
2、系统内存空间不足时(将要发生oom之前),回收这些对象的内存。如果回收后还是没有足够的内存这时才会抛出oom error。
3、软引用可以和引用队列ReferenceQueue联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中- 使用场景:用于内存敏感的高速缓存。在 jvm 报告内存不足之前会清除所有的软引用,这样以来 gc 就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
//SoftReference 简单使用
String str = new String("aaa");
SoftReference<String> softReference = new SoftReference<>(str,referenceQueue);
String s = softReference.get();//垃圾回收了这里就返回null,不回收就返回相应对象
System.out.println(s);
Reference<? extends String> reference = referenceQueue.poll();
补充:
- 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。
- 什么时候会被收集取决于 gc 的算法和 gc 运行时可用内存的大小
- 对 ReferenceQueue 软引用和弱引用可以有可无,但是虚引用必须有。
3、弱引用(WeakReference)
- 作用:用来描述非必要的对象
- 特点:
1、如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干掉。
2、只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存,不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
3、 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中- 使用场景:
1、防止内存泄漏:内部类非静态对象持有外部类引用
2、监控对象是否将要被回收
//WeakReference 基本使用
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
String str = new String("aaa");
WeakReference<String> weakReference = new WeakReference<>(str,referenceQueue);
String s = weakReference.get();
System.out.println("s:"+s);
System.gc();//通知系统进行垃圾回收,但是系统不一定执行
System.out.println("isEnqueued:"+weakReference.isEnqueued());// 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中
System.out.println(weakReference.get());
4、虚引用(PhantomRefrence 对)
特点:
1、虚引用并不会决定对象的生命周期,即无法通过虚引用来获得对象的实例。
2、如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
作用:
虚引用主要用来跟踪对象被垃圾回收的活动。当垃圾回收器回收对象时虚引用会收到系统通知。
使用场景:判断对象是否将要被回收。
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动,也可以理解为对象回收的一种回调方法。
String string = new String("aaa");
ReferenceQueue queue = new ReferenceQueue();
PhantomReference phantomReference = new PhantomReference(string, queue);
string = null;
String s1 = (String) phantomReference.get();//null
System.out.println("s1:"+s1);
// 判断是否入队(入队则表示要被jvm回收)
System.out.println("isEnqueued:"+phantomReference.isEnqueued());
Object的finalize
1、方法介绍
Object的方法,内部空实现。任何Java类都可以重写Object类的finalize()方法,在该方法中清理该对象占用的资源。当finalize()方法返回后,对象消失,垃圾回收机制开始执行。
protected void finalize() throws Throwable { }
2、finalize 方法特点
- 不要主动调用某个对象的finalize()方法,该方法是垃圾回收机制在回收某个对象之前进行调用。由jvm调用。
- finalize()方法何时被调用,是否被调用具有不确定性,不要把finalize()方法当成一定会被执行的方法。
- 当JVM执行可恢复对象的finalize()方法时,可能使该对象或系统中其他对象重新变成可达状态(finalize方法中进行了某些对象自救)
- 当JVM执行finalize()方法时出现异常时,垃圾回收机制不会报告异常,程序继续执行。
场景解释:
1、如果程序终止之前始终没有进行垃圾回收,则不会调用失去引用对象的finalize()方法来清理资源。
2、某个失去引用的对象只占用了少量内存,而且系统没有产生严重的内存需求,因此垃圾回收机制并没有试图回收该对象所占用的资源,所以该对象的finalize()方法也不会得到调用
触发demo:
/**
* Create by SunnyDay on 2020/10/09
*/
public class FinalizeMethod {
public static void main(String[] args) {
Person person = new Person();
ReferenceQueue referenceQueue = new ReferenceQueue();
PhantomReference<Person> personPhantomReference = new PhantomReference<>(person, referenceQueue);
person = null;
System.gc(); // 通知系统,建议gc
}
}
class Person {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
}
// log:
finalize
Process finished with exit code 0
总结:finalize 由jvm调用,调用具备不确定性。不建议开发者使用。