Java面试问题总结——介绍一下什么是强引用和弱引用?它们的用法是什么?

目录

一、什么是强引用,软引用,弱引用,虚引用?

1.强引用(StrongReference)

2.软引用(SoftReference)

3.弱引用(WeakReference)

4.虚引用(PhantomReference)

5.四种引用的区别

二、引用的使用场景

1.强引用(StrongReference)

2.软引用(SoftReference)

3.弱引用(WeakReference)

4.虚引用(PhantomReference)


这个问题是阿里菜鸟网络一面面试官的提问,由于个人准备不够充分以及知识储备不足没有答出问题根本。其实这个问题是Java面试中比较常见的问题,按难度来划分的话应该只是中等难度,之前也了解过它们的区别,但是实际开发过程几乎很少使用导致只是纯粹记忆,本文将系统的整理强引用,软引用,弱引用和虚引用的区别,并结合具体场景探究一下它们的用法。

一、什么是强引用,软引用,弱引用,虚引用?

JDK1.2 以前,如果一个对象不被任何变量引用,则程序无法再次使用这个对象,这个对象最终会被 GC(GabageCollection:垃圾回收)。但是如果之后可能还会用到这个对象,就只能去新建一个了,这其实就降低了 JVM 性能,没有达到最大的优化策略。

JDK1.2 开始,提供了四种类型的引用:强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference)。主要有两个目的:1.可以在代码中决定某些对象的生命周期;2.优化JVM的垃圾回收机制。

1.强引用(StrongReference)

强引用是程序代码之中普遍存在的,我们一般创建对象的过程都是强引用,如下面代码。

    Object object = new Object();
    String str = "123";  

如果某个对象有强引用与之关联,Java虚拟机(JVM)必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory 错误也不会回收这种对象。基于上述情况,我们在使用完对象后如果想让 JVM 回收对象需要将对象弱化,具体操作是将其引用置为null。(补充说明:局部方法内的强引用会随着方法的结束退栈而自动清除引用,全局变量需要在不使用时置null

    Object strongReference = new Object();
    strongReference = null;

Java集合类的强引用弱化方式不能采取置 null 方式,因为这种方式强引用依然存在,只有采用 clear()方法内存数组中存放的引用类型进行内存释放,这样才可以及时释放内存。

        ArrayList arrays = new ArrayList<>(10);
        arrays.add(1);
        arrays.add(2);
        arrays.add(3);
        arrays.clear();//不使用时 clear 清除数组

查看 ArrayList clear 方法的源码发现是遍历元素数组将每个元素置为 null ,即 elementData[i] = null

2.软引用(SoftReference

软引用是用来描述一些有用但并不是必需的对象,适合用来实现缓存(比如浏览器的‘后退’按钮使用的缓存),JVM内存空间充足的时候将数据缓存在内存中,如果空间不足了就将其回收掉。

    // 强引用
    String strongReference = new String("123");
    // 软引用,"456"这个String对象包含一个强引用 str 和弱引用 softReference 
    String str = new String("456");
    SoftReference softReference = new SoftReference(str);

软引用可以和一个引用队列(ReferenceQueue)联合使用。如果软引用所引用对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。

    ReferenceQueue referenceQueue = new ReferenceQueue<>();
    String str = new String("abc");
    SoftReference softReference = new SoftReference<>(str, referenceQueue);

    str = null;//去掉 str 的强引用
    System.gc();// Notify GC

    System.out.println(softReference.get()); // abc

    Reference reference = referenceQueue.poll();
    System.out.println(reference); //null

软引用对象是在jvm内存不够的时候才会被回收,我们调用System.gc()方法只是起通知作用,JVM什么时候扫描回收对象是JVM自己的状态决定的。即使扫描到软引用对象也不一定会回收它,只有内存不够的时候JVM才会回收。

3.弱引用(WeakReference)

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象,在java中用java.lang.ref.WeakReference 类来表示。

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期,它只能生存到下一次垃圾收集发生之前。当垃圾回收器扫描到只具有弱引用的对象时,无论当前内存空间是否足够,都会回收它。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

    String str = new String("abc");
    WeakReference weakReference = new WeakReference<>(str);
    str = null;//去掉强引用

如果一个对象是偶尔(很少)的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么我们应该用WeakReference 来标记此对象。

弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

    String person = new String ("123");
    ReferenceQueue queue = new ReferenceQueue<>();
    WeakReference weakReference = new WeakReference(person, queue);

    person = null;//去掉强引用

4.虚引用(PhantomReference)

与其他三种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃 圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

    String str= new String();
    ReferenceQueue queue = new ReferenceQueue ();
    // 创建虚引用,要求必须与一个引用队列关联
    PhantomReference pr = new PhantomReference (str, queue); 

虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

5.四种引用的区别

根据上面对四种引用的介绍,按照引用被回收的机制特点总结了它们的区别如下。

级别 何时被GC回收 生成时间
强引用 从来不会 JVM停止时
软引用 内存空间不足时 内存不足时终止
弱引用 对象未被引用时 GC后终止
虚引用 对象被回收时 内存使用控制引用队列对象被回收

二、引用的使用场景

1.强引用(StrongReference)

强引用是我们日常代码中最常见的,这里不再赘述,只需要注意当强引用对象不再使用需要回收时要将引用变量置为 null,另外集合对象的置空需要使用 clear 方法。

2.软引用(SoftReference

软引用的回收机制使得它非常适合用于创建缓存,当系统内存不足的时候,缓存中的内容是可以被释放的。

  • 图片缓存。图片缓存框架中,“内存缓存”中的图片是以这种引用保存,使得 JVM 在发生 OOM 之前,可以回收这部分缓存。
  • 网页缓存。
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);       // 重新构建
}

3.弱引用(WeakReference)

当发生GC时,弱引用对象总会被回收,因此弱引用也可以用于缓存。

  •  ThreadLocalMap防止内存泄漏
  • 监控对象是否将要被回收
Object obj = new Object();
WeakReference wf = new WeakReference(obj);//创建弱引用
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
System.gc();  //通知JVM的gc进行垃圾回收,但JVM不一定会立刻执行
wf.get();//此时会返回null 
  

4.虚引用(PhantomReference)

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

  • 它允许你知道具体何时其引用的对象从内存中移除。这一点尤其表现在处理类似图片的大文件的情况。当你确定一个图片数据对象应该被回收,你可以利用虚引用来判断这个对象回收之后在继续加载下一张图片。这样可以尽可能地避免可怕的内存溢出错误。

  • 虚引用可以避免很多析构时的问题。finalize 方法可以通过创建强引用指向快被销毁的对象来让这些对象重新复活。然而,一个重写了 finalize 方法的对象如果想要被回收掉,需要经历两个单独的垃圾收集周期。在第一个周期中,某个对象被标记为可回收,进而才能进行析构。

你可能感兴趣的:(Java面试问题总结,Java基础)