关于Java中引用的面试题
一、介绍
在Java中,有以下四种类型的引用:强软弱虚
- 强引用(Strong Reference):最常见的引用类型,也是默认的引用类型。如果一个对象具有强引用,那么垃圾回收器就不会回收这个对象。
- 软引用(Soft Reference):如果一个对象具有软引用,那么当系统内存不足时,垃圾回收器会尝试回收该对象。软引用通常用于缓存中,以便在内存紧张时释放一些缓存。
- 弱引用(Weak Reference):如果一个对象具有弱引用,那么它的生命周期更短,它在任何时候都可能被垃圾回收器回收。弱引用通常用于外部引用内部对象时使用,以免内存泄漏。
- 虚引用(Phantom Reference):虚引用是所有引用类型中最弱的一种。如果一个对象具有虚引用,那么它就像没有被引用一样,随时会被垃圾回收器回收。虚引用主要用于跟踪对象被回收的状态。
上面属于Java
的面试八股文,那么在面试之中,我们该如何进行理解输出呢?
二、引用
1)强引用(Strong Reference)
在日常开发中最为平常的引用,因为我们直接new
出来的对象就属于强引用。
那么,如果一个对象只要有强引用,那么GC
就不会回收掉它。如下这个类
package com.banmoon.reference;
public class Reference {
@Override
protected void finalize() throws Throwable {
System.out.println("GC回收");
super.finalize();
}
}
我只有将引用设置为null
后,GC
才能回收掉它,强引用就是如此。
package com.banmoon.reference;
import java.io.IOException;
public class StrongReference {
public static void main(String[] args) throws IOException {
Reference reference = new Reference();
System.out.println(reference);
// 赋值为null,取消对象的强引用
reference = null;
// 通知进行gc
System.gc();
System.out.println(reference);
// 阻塞一下
System.in.read();
}
}
2)软引用(Soft Reference)
软引用和强引用不同
- 强引用只要有引用指向对象,对象就不会被回收
- 而软引用,就算有软引用指向对象,在发生内存不足的时候,
GC
就会把这些软引用的对象给回收
为了测试下面的代码,我们需要添加一点JVM
参数,限制一下JVM
的内存,即-Xms20M -Xmx20M
,我限制了20M
的内存
package com.banmoon.reference;
import java.lang.ref.SoftReference;
import java.util.concurrent.TimeUnit;
public class SoftReferenceMain {
public static void main(String[] args) throws InterruptedException {
// 一个byte数组,内容大小是10M
SoftReference<byte[]> sr = new SoftReference<>(new byte[1024 * 1024 * 10]);
// 获取数组
System.out.println(sr.get());
// 睡眠一秒后,再次查看数组
TimeUnit.SECONDS.sleep(1);
System.out.println(sr.get());
// 我再创建一个数组,12M,再次查看原来软引用的数组
byte[] bytes = new byte[1024 * 1024 * 12];
System.out.println(sr.get());
}
}
3)弱引用(Weak Reference)
比起上面的两个引用,弱引用可以这样理解,它引用的对象,只要发生GC
,就都会被回收。
也就是说,前两个引用都一定程度上保护了对象,但弱引用不行,弱引用保护不了任何对象。
在平常的使用中,基本没啥用,当然ThreadLocal
中使用到了,搭配着强引用一起进行使用的。
简单改造一下强引用的代码,变成弱引用,GC
后会发生什么
package com.banmoon.reference;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
public class WeakReferenceMain {
public static void main(String[] args) throws IOException, InterruptedException {
WeakReference<Reference> wr = new WeakReference<>(new Reference());
System.out.println(wr.get());
// 通知进行gc,并阻塞一下
System.gc();
TimeUnit.SECONDS.sleep(1);
// 查看输出
System.out.println(wr.get());
}
}
4)虚引用(Phantom Reference)
最后一个虚引用,比较特殊。主要是给GC
使用的,对的没错,JVM
在GC
的时候,也会创建对象,这些基本就是虚引用。
下面作为示例了解一下
代码语言:javascript复制package com.banmoon.reference;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class PhantomReferenceMain {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Reference> queue = new ReferenceQueue<>();
PhantomReference<Reference> phantom = new PhantomReference<>(new Reference(), queue);
//检查垃圾回收队列中是否已经加入该对象
System.out.println("Is queued: " phantom.isEnqueued());
//强制进行垃圾回收并等待GC完成
System.gc();
Thread.sleep(1000);
//检查垃圾回收队列中是否已经加入该对象
System.out.println("Is queued: " phantom.isEnqueued());
//从队列中读取并打印垃圾回收的信息
System.out.println(queue.poll());
}
}
虚引用的使用场景 虚引用通常用于实现比弱引用更加精细的对象 finalization(终结)处理逻辑。虚引用通常与引用队列结合使用,对于一个具有虚引用的对象,当垃圾回收器准备回收该对象时,如果发现它存在虚引用,就会在回收对象的内存之前,将这个虚引用加入到与之关联的引用队列中。 在实际应用中,虚引用常用于:
- 用于在对象被回收时进行一些定制操作,例如发送通知、记录日志、清理资源等等。
- 用于避免内存泄漏,通过使用虚引用表示该对象将会被垃圾回收器回收,并触发一些清理操作。
值得注意的是,虚引用并不会影响被引用对象的生命周期。当垃圾回收器准备回收对象时,虚引用会被加入到与之关联的引用队列中,但此时虚引用本身并不能保证被回收,需要不断调用getReference()
方法来获取引用队列中的虚引用,直到返回null为止。
三、最后
关于GC
回收强引用的对象,有时候就算被强引用,也还是会被回收的场景,比如说循环引用。所以还是得具体情况,具体分析。
我是半月,你我一同共勉!!!