JVM四种引用
如果一个对象存在引用,那么该引用对象不会被回收? – 局限性
只适用于强引用对象。
强引用
Object obj = new Object(); // 一般使用new
约定: 引用obj , 引用对象new Object()
强引用对象(new Object())什么时候失效?
1. 生命周期结束(作用域失效)
public void method() {
Object obj = new Object();
} // 方法执行完毕,强引用对象指向的引用对象就会等待被gc回收
2. 引用置为null
obj = null; // 引用对象等待被gc回收
除了以上两种情况,其他任何时候(即使JVM内存溢出)gc都不会回收强引用对象。
软引用
软引用: 内存不足时会回收
解决强引用的强势问题 ===> 除了两种情况,任何时候(即使JVM内存溢出)gc都不会回收强引用对象
内存不足了,即使还在使用,也可能会被回收。
可作为缓存淘汰策略(内存不足,gc自动回收)
java.lang.ref.SoftReference
装饰者模式
// 构造器层层套娃
// 装饰者模式: 强引用对象包裹一下成为软引用对象
SoftReference<SoftObject> softReference = new SoftReference<>(new SoftObject());
// io中的装饰者模式: 字符串 -> 文件 -> 文件流 -> 转换流(字符流) -> 缓冲字符流
//new BufferedReader(new InputStreamReader(new FileInputStream(new File("c:\\program\\xxx"))));
验证
public static void main(String[] args) throws FileNotFoundException {
// 装饰者模式:构造器层层套娃, 强引用对象包裹一下成为软引用对象
// new BufferedReader(new InputStreamReader(new FileInputStream(new File("c:\\program\\xxx"))));
SoftReference<SoftObject> softReference = new SoftReference<>(new SoftObject());
// SoftObject obj = softReference.get();// 返回new SoftObject()对象
ArrayList<byte[]> list = new ArrayList<>();
// 验证
new Thread(() -> {
while (true) {
// **********这里必须加上一段内容,防止while死循环导致该工作内存无法同步softReference.get()值
// **********也不要使用obj值替换softReference.get()
System.out.println("-----");
if (softReference.get() == null) { // 如果对象被回收,退出jvm
System.out.println("软引用对象被回收");
System.exit(0);
}
}
}).start();
while (true) { // -Xmx=128m -Xms=64m
if (softReference.get() != null) {
list.add(new byte[1024*1024]);
}
}
}
弱引用
只要gc执行,弱引用就会被回收(不管内存足不足)
public static void main(String[] args) {
WeakReference<Object> weakReference = new WeakReference<>(new Object());
System.out.println(weakReference.get() == null ? "已被回收": "未被回收");
System.gc(); // 建议gc执行一次回收(存在概率)
System.out.println(weakReference.get() == null ? "已被回收": "未被回收");
}
虚引用
幻影引用或者幽灵引用
java.lang.ref.PHantomReference<T>
是否使用虚引用,和引用对象本身没有任何关系;
无法通过虚引用来获取对象本身
虚引用get() = null
虚引用不会单独使用,一般会和引用队列ReferenceQueue一起使用
价值:当gc回收一个对象,如果gc发现此对象还有一个虚引用,就会将虚引用放入引用队列中,之后(当虚引用出队后)再去回收该对象。
因此,我们可以使用虚引用+引用队列实现在对象被gc回收之前,进行一些额外的操作
GC -> 如果有虚引用 -> 虚引用入队 -> 虚引用出队 -> 回收对象
特殊情况:如果虚引用对象重写了finalize方法,那么引用对象入队时机就会被延迟,可能是延迟到下一次执行gc
验证
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
// 引用队列
ReferenceQueue queue = new ReferenceQueue();
// 虚引用 + 引用队列
PhantomReference<Object> phantomReference = new PhantomReference<>(obj, queue);
// gc执行
obj = null;
System.gc();
Thread.sleep(30);
System.out.println("gc执行");
// 执行入队...
// GC -> 如果有虚引用 -> 虚引用入队 -> 虚引用出队 -> 回收对象
System.out.println(queue.poll());
}