共计 2305 个字符,预计需要花费 6 分钟才能阅读完成。
【干货点】看完该篇文章,就基本可以解答面试热点【谈谈对 Java 中几种引用的理解】了。
大家都知道我公众号的副业是
所以经常会有朋友找我吹水,最近就说到了一个面试题
谈谈对 Java 中几种引用的理解。
因此打算以该面试题为例子,写篇文章说说在 Java 中引用是什么以及怎么用。
【划重点:给你三秒钟时间思考,如果是你,该如何回答面试官的这个问题呢】
引用是什么
【划重点】在 Java 中引用包括:
- FinalReference 强引用
- SoftReference 软引用
- WeakReference 弱引用
- PhantomReference 虚引用
那么为什么会提供这四种引用呢,主要原因有:
- 方便 Jvm 进行垃圾回收
- 方便开发人员使用,开发人员可以灵活的决定某些对象的生命周期
日常开发如何使用
学以致用,那么这几个引用在日常中我们如何进行使用呢?
接下来我会给出相关 demo!!!
FinalReference 强引用
类似于 Object o = new Object() 这类的引用,创建一个对象后,该引用会被保存在 JVM 栈中,而且只要强引用存在,垃圾回收器就不会回收掉被引用的对象。
日常使用
强引用的例子比比皆是,因为在日常开发中我们是会经常去 new 一个对象的,而该 new 出来的对象便是强引用的,也就是说只要该引用存在,垃圾回收器就不会回收掉。
【划重点:JVM 怎么知道引用在不在?】
SoftReference 软引用
软引用关联的对象,在内存不够的情况下,会把这些软引用关联的对象列入垃圾回收范围中,然后进行回收,也就是说软引用并非是完全安全的,在内存不够的情况下是会被垃圾回收器回收掉的。
给出 demo
通过注释便可以知道,我这里实例化了多个大对象,然后放入 softReferences 数组中,之后便遍历打印出其中的对象的命名,打印结果如下
可以通过结果看出,前面四个对象因为内存不够而被垃圾回收器回收了。
日常使用
在我司的项目中,部分是使用软引用来保存从数据库中取出的数据,具体是做了一个中间层的封装,该中间层的作用就是在 get 出数据的时候会去判断数据是否为 null,如果是为 null 再次从数据库读取,读取后再放入软引用的集合中,这样的做法是可以避免内存溢出。
WeakReference 弱引用
弱引用比软引用更弱,被弱引用关联的对象只能存活到发生下一次垃圾回收之前,也就是说当发生 GC 时,无论当前内存是否足够,都会被回收掉。
给出 demo
代码很简短,就是先构建一个弱引用对象,然后在 gc 前先打印出来证明它存在过,之后手动调用 gc,再次打印,可以看出已经没了。运行结果如下
PhantomReference 虚引用
虚引用和上面不同的地方在于,一个对象是否有虚引用的存在,完全不会对其生存时间构成如何影响,并且也无法通过虚引用来获取一个对象的实例,也就是说跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
那么这样就很容易产生疑问了,虚引用的作用又是什么呢?
作用就是能在这个对象被收集器回收时收到一个系统通知,实现追踪垃圾收集器的回收动作,比如在对象被回收的时候,会调用该对象的 finalize 方法。
在给出相关 demo 前,要先介绍一个
ReferenceQueue 引用队列
ReferenceQueue 引用其实也可以归纳为引用中的一员,可以和上述三种引用类型组合使用【软引用、弱引用、虚引用】。
那么它有何作呢?
在创建 Reference 时,手动将 Queue 注册到 Reference 中,而当该 Reference 所引用的对象被垃圾收集器回收时,JVM 会将该 Reference 放到该队列中,而我们便可以对该队列做些其他业务,相当于一种通知机制。
给出 demo
可以从 demo 中看出队列的用法,运行打印结果如下
我们可以从结果中看到先是从引用中 get 出来的对象为 null,证明上面说的无法通过虚引用来获取一个对象的实例,并且在回收后会被放入队列中。
和 Reference 相关的概念
首先为了方便 JVM 进行管理,Reference 是有状态的,可以分为以下四种状态
- active 一般来说内存一开始被分配的状态,而当被引用的对象的可达性发生变化后 gc 就会将引用放入 pending 队列并将其状态改为 pending 状态。
- pending 指的是准备要被放进 pending 队列的对象。
- enqueue 指的是对象的内存已经被回收了。
- inactive 这是最终的状态,不能再变为其它状态。
JVM 怎么知道引用在不在
关于 JVM 怎么知道引用在不在,这就涉及到了 JVM 的可达性分析算法了
JVM 的可达性分析算法的简单思路就是通过一系列 GC Roots 作为出发点,向下搜索,搜索所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链,即表明从 GC Roots 到这个对象不可达时,证明此对象不可用,可被回收。如下图所示
对象 4、5、6 都是可被回收的。
那么问题来了,哪些对象可以作为 GC Roots 呢?
这里给出几个,如下
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈 JNI 引用的对象
具体的想要深入研究的可以自行百度 & 谷歌,或者等我后面深入分析。
最后的最后
该篇文章基本解答了【谈谈对 Java 中几种引用的理解】,如果想要更深入的研究,就要从源码入手了解了。
下次遇见这种面试题,基本上就不慌了,因为实际上只要认真看完该篇文章并且记住几个关键的地方,基本上就不会被面试官问倒了,并且该篇文章后面也解答了【JVM 怎么知道引用在不在】和【哪些对象可以作为 GC Roots】的问题。
公众号主营:服务端编程相关技术解说,具体可以看历史文章。
公众号副业:各种陪聊吹水,包括技术、就业、人生经历、大学生活、内推等等。
欢迎关注,一起侃大山