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