Java垃圾收集器和参考对象
通过优锐课核心java学习笔记中,我们可以看到,码了很多专业的相关知识, 分享给大家参考学习。
我们来看一下Java中的垃圾收集器概念,以及它可以处理的各种引用对象类型,这使Java GC神秘化!
在本文中,我们将讨论Java中的一些内存管理概念,并将重点放在垃圾收集器与可用的不同参考对象之间的交互上。
这不是介绍,所以让我们相互同意,你已经掌握了Java Heap和GC基础知识。 许多文章都很好地涵盖了该主题,你实际上可能想知道为什么要覆盖已经在www上进行了充分讨论的内容。
- 我发现大多数文章都很好地介绍了Java内存,但是后来开始对诸如引用对象之类的东西感到窒息。 这是“谁不知道这些东西?”态度的一种变体,或者是作者的疲倦。 我想尽我所能,但愿不要陷入同一池中。
- 记忆是高级开发人员面试问题的金矿。 “ Java管理自己的内存,我真的不必知道它是如何做到的。” 给邻居或密友的好故事。 祝你好运的让面试官信服。 寓意:你必须了解记忆。
类似
类比使对计算概念的理解简短而甜蜜,充满了哦! 呵呵! 片刻。 希望你能有所体验。
想象学校的自助餐厅。 餐盘稀缺,但经理很聪明。 他与他的员工一起制定了一项策略,目的是:及时为所有饥饿的学生提供食物,而不会因为盘子不足而使任何人缺粮。
策略0:他们计划在学生吃完饭并离开食堂后收集所有用过的盘子进行洗涤,以使尚未吃过的盘子重新使用。
因此,每当服务团队报告餐盘用完时,都会派出专门的女服务员来收集所有用过的餐盘。 只要正在使用盘子的学生离开自助餐厅,便会收集盘子。 然后将这些盘子清洗并添加到堆中,以服务更多的学生。
该策略非常有效,经理先生对自己和他的员工感到满意。 很快,他意识到一些学生吃完饭了,但仍坚持与朋友聊天和大笑。 他已经告诉他的收藏家女服务员只在学生离开桌子时才收集盘子。 结果,许多脏盘子被卡在桌子上,仅仅是因为满意的学生仍然坐在那里,而自助餐厅经常遇到盘子危机。
策略1: 经理先生还有另外一招。 他对收集服务员的指示发生了变化:只要学生吃完饭就收集盘子,无论他/她是否仍然坐在桌子上。 学生听到了新的和``敌对的''变化的消息,许多特殊群体都在抱怨与经理先生达成妥协。 该策略被标记为失败。
策略2: 作为一个聪明的人,他提出了更出色的想法,并为任何收藏家女服务员制定了一套指导方针:
- 如果学生是省长或行会代表,请允许他们将餐盘保留任意长的时间,直到他们明确要求你领取餐盘或离开餐桌为止。
- 如果学生是州长或行会代表的女友或男友,则让他们享有伴侣的特权……这意味着,即使他们已经吃完饭但仍在餐桌上,除非有绝对必要,也不要着急收拾盘子。没有其他选择,自助餐厅实际上在可用盘子方面已达到极限。
- 如果学生不是校长,并且不与任何校长有任何关系,就像大多数第一年级的情况一样,请非常警惕收集自己的盘子-不要拖延,不要妥协。只要他们吃完饭,无论是否乞求哭泣,他们是否仍然坐在桌子上。不允许他们拥有任何特权。
- 最后,医生已向糖尿病学生发送了一份清单,需要了解他们每天最后一餐的确切时间,以便确定何时进行常规血糖水平检查。就像你为上一个类别的学生一样,收集他们的盘子,但是在记下盘子并通知我时,请务必记下确切的时间。
- 原来这是一个事实
事实证明,这是一个极好的策略。 讨论迭代式问题解决。
自助餐厅中的学生对Java对象的引用遵循一定的强度和特权层次。 让我们深入探讨技术故障。
强大的参考
在每个Java程序中,对象都是保存和操作数据的方式:
StringBuilder sb = new StringBuilder();
在上面的代码片段中,新关键字创建了一个StringBuilder对象,并将对其的强大引用存储在变量sb中。 这是我们创建的所有对象的默认强度级别,因此我们不会像其余部分那样使用任何特殊标签来标识它们。
要在层次结构中创建其余引用,我们需要位于java.lang.ref包中的特殊包装对象。
具有至少一个强引用的任何对象均不符合垃圾回收的条件。 以我们的类推,州长对他们的盘子有强烈的参考。 用技术术语来说,我们说该对象是可以达到的。
仅当我们取消其引用时才有资格进行收集,类似于离开该表的主管:
sb = null;
软参考
可以通过将对象的实例包装在java.lang.ref.SoftReference对象中来创建对对象的软引用:
StringBuilder sb = new StringBuilder();
SoftReference<StringBuilder> sbSoftRef = new SoftReference<>(sb);
sb = null;
在上面的代码段中,第一行创建了一个字符串构建器对象,该对象带有一个存储在sb中的强引用。 第二行在sbSoftRef中创建一个对其的软引用,因此字符串生成器对象现在具有两个引用。
在此阶段,字符串生成器不符合收集条件。 但是,第三行使强引用无效,现在该对象仅具有软引用。
现在,该物品类似于坐在县长的男朋友/女友面前的用过的盘子-只有在食堂工作人员绝对确定没有更多可用的盘子时,它只能作为最后的手段收集。 从技术上讲,我们说该对象可以软到达。
在此阶段,我们仍然可以通过调用SoftReference对象的get方法来检索对该对象的强引用,如果已经收集了该对象,则返回null:
sb = sbSoftRef.get();
仅当JVM得出结论认为没有更多内存可分配给新对象并且处于抛出OutOfMemory错误的边缘时,才会收集仅具有软引用的对象。
软引用旨在用于对内存敏感的缓存中。 随着高速缓存的增长,用于新对象的可用内存会减少,但你仍然需要高速缓存,因此JVM会以“你”妥协,直到无法再使用为止。
参考不足
通过将对象的实例包装在java.lang.ref.WeakReference对象中,可以创建对对象的弱引用:
StringBuilder sb = new StringBuilder();
WeakReference<StringBuilder> sbWeakRef = new WeakReference<>(sb);
sb = null;
在以上代码段中,在第三行中取消了强引用之后,该对象立即变为有资格使用GC。
现在,该对象类似于没有特权地坐在一年级学生面前的旧盘子。 可以立即收集它,而无需考虑学生是否仍坐在桌子旁。 从技术上讲,我们说该对象微弱可达。
尽管我们仍然可以检索到对对象的强引用,但是机会窗口远小于软引用的窗口。 与其他情况相比,我们得到空值的频率要高得多:
sb = sbWeakRef.get();
无论内存是否紧张,GC都会积极收集只有弱引用的对象。
弱引用旨在用于规范化映射。 仅仅5分钟的谷歌搜索就可以使你对该用例有一个了解,因此我不愿赘述。
幻影参考
通过将对象的实例包装在java.lang.ref.PhantomReference对象中,可以创建对象的幻像引用:
StringBuilder sb = new StringBuilder();
ReferenceQueue<StringBuilder> refQ = new ReferenceQueue<>();
PhantomReference<StringBuilder> sbPhantomRef = new PhantomReference<>(sb, refQ);
sb = null;
在上面的代码段中,在第四行中取消了强引用之后,该对象立即可以使用GC。忽略稍后的“ ReferenceQueue”对象。现在,请记住,与软引用和弱引用不同,PhantomReference没有参考队列是没有用的。
现在,该对象类似于坐在一位患有糖尿病的一年级学生面前的旧盘子。即使有一个条件,也可以立即收集,而无需考虑学生是否仍坐在桌子旁:记录了确切的收集时间并通知了文档。
只要已注明时间的纸还没有被医生使用和丢弃,我们就将该对象称为幻影可达。 (主要的困惑,请继续阅读但要当心)。
PhantomReference对象的get方法是无用的,因为它总是返回null。相对于软和弱参考,这进一步增强了该参考对象的独特性。下一节将使这种独特性更加清晰。
虚拟引用旨在用作Object.finalize()方法的一种更灵活的替代方法。
参考队列
顾名思义,引用队列是一种数据结构,它使引用对象入队:WeakReference,SoftReference和PhantomReference。
是否随时将参考对象加入队列取决于我们在创建参考对象时是否提供了ReferenceQueue对象。除了PhantomReference之外,提供一个也不是强制性的,甚至没有用。
根据参考对象的类型,将其排队的确切点会有所不同。除了扩大幻象引用的范围之外,我无意在此主题上做太多介绍。认真阅读下一段。
所引用的对象由垃圾收集器完成后,就会将幻像引用加入队列。 “最终化”是指其finalize()方法已被调用。 GC会在收集对象之前调用对象的finalize()方法,以帮助创建对象的应用程序受益。好处是有机会释放对象在其尘世生命中必须创建或使用的非“ GC有效”资源(读取Java堆生命)。 GC无法访问无法使用“ GC”的资源。一个明显的例子是操作系统提供的文件句柄。为了说明这一点,请看一下FileInputStream类的finalize方法:
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
注意最后的close()方法调用,看起来很熟悉吗?是的,正是Java老师告诉你的fis.close()始终放在try / catch / finally块的最终子句中。想知道这是否是重复调用,因为你总是关闭()文件资源?太好了,继续阅读!
现在注意方法主体顶部的if检查。如果你已经在代码内部调用了close(),则上述代码中的fd将为空,并且方法主体的其余部分将不会执行。呵呵!!!! (来不及了??)。继续阅读!
碰巧的是,最近,finalize()方法(特别是在上述FileInputStream之类的库/平台类中)充当了安全网,适用于我们作为开发人员鲁ck而忘记释放非堆资源的情况。通过低声告诉你,你可能永远不必使用幻像ref,而继续阅读)。
因此,所有这些与幻影引用和引用队列有什么关系。我失去焦点了吗?好吧,你问,所以我会回答:
Finalize()方法困扰着众多问题,这些问题几乎抵消了它试图提供的所有优势。实际上,约书亚·布洛赫(Joshua Bloch)的《有效的Java》(Effective Java)一书中有详细的报道,并且许多博客都在广泛讨论其问题。不要以为他们只是在弄脏本来不错的API的图像。除非你是即将上任的詹姆斯·高斯林(James Gosling),否则我强烈建议你只听从他们的话,并与其他选择相处融洽,以善用资源。现在,继续阅读-我们差不多完成了。
我们现在可以在这部分中进行finalize()解释为什么引入幻影引用和引用队列的原因。创建PhantomReference对象是为了“为finalize()提供一个更灵活的替代方法。”无论成功与否,这都是一个很大的话题,但是我敢打赌没有。
作为程序员,我们如何做到的是在创建PhantomReference实例的同时提供一个ReferenceQueue实例,就像我们之前看到的那样。调用对象的finalize()方法后,其幻影引用就入队。我们有责任继续轮询参考队列以跟踪此事件的发生时间。这是我们手动释放资源的地方,包括对phantomRef.clear()方法的最终调用以使基础引用无效。
差异汇总
我知道本节的标题有点蹩脚,但请多多包涵。 我将尝试汇总三个参考对象的差异,以消除任何疑问
·排队:一旦软GC和弱引用对象从其算法“标记”了它们的内存对象以进行收集,但尚未完成,则将它们排队。另一方面,一旦确定了内存对象,就将幻像引用加入队列。
·Reference.get():软引用和弱引用的get方法都返回对基础对象的强引用;如果该对象已被标记为要收集,则返回null。另一方面,幻影引用的getmethod始终返回null(无论是Josh Bloch还是Jon Skeet都在称呼它。开玩笑!)。这种差异触及了他们的用例的关键点,对于弱引用和软引用都有用,一个人应该能够在“机会之窗”内为其对象重新创建强引用。但是幻影引用不需要我们之前探讨的质量。
·Reference.clear():此方法在所有三个ref对象中均可用。它将基础引用设置为null,因此显式地利用内存对象在其时间之前进行收集。对于弱引用和软引用,这是无用的功能,因为它会抵消它们的好处。但这是幻影引用的关键,因为你必须在清理步骤的最后将其称为幻影先生。
·ReferenceQueue:对于软和弱引用,它是没有用的,但是没有它,幻像引用是没有用的。
·用例:软引用:内存敏感的缓存,弱引用:规范化的映射,幻像引用:替代finalize()的更灵活方法。
结论
在这篇尽管篇幅很长的文章中,我试图讨论我关于GC与参考对象之间关系的发现。 我希望它至少能使你更好地理解参考对象以及GC。
不用说,你可能永远不必直接处理以下引用类型:
- ·需要内存敏感的缓存吗? (顺便说一下,这是一项非常复杂而细致的努力)。使用诸如ehcache之类的扎实且经过艰苦训练的库。
- ·需要规范化的映射? 只需使用java.util.WeakHashMap即可避免脱发。
- 要进行事前清理吗? 请勿被Phantom先生的产品所欺骗,而不要被finalize()所欺骗,请坚持使用古老的尝试/捕捉/最终。
> 喜欢这篇文章的可以点个赞,欢迎大家留言评论,记得关注我,每天持续更新技术干货、职场趣事、海量面试资料等等
> 如果你对java技术很感兴趣也可以交流学习,共同学习进步。
> 不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代
文章写道这里,欢迎完善交流。最后奉上近期整理出来的一套完整的java架构思维导图,分享给大家对照知识点参考学习。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java干货