Java垃圾收集器和参考对象

在本文中,我们将讨论Java中的一些内存管理概念,重点是垃圾收集器与可用的不同参考对象之间的交互。
这不是介绍,因此让我们彼此同意,您应该了解Java Heap和GC的基础知识。许多文章都很好地涵盖了该主题,您实际上可能想知道为什么要覆盖已经在www上进行了充分讨论的内容。
我发现大多数文章都很好地介绍了Java内存,但是后来开始对诸如引用对象之类的东西感到窒息。它是“谁不知道这些东西?”的变体。态度或作者疲劳。我想尽我所能,但愿不要陷入同一池中。
记忆是高级开发人员职位面试问题的金矿。“ Java管理自己的内存,我真的不必知道它是如何做到的。 ”对于您的邻居或密友来说,这是个好故事。祝你好运的面试官信服。道德:您必须了解记忆。
比喻
类比使对计算概念的理解简短而甜蜜,充满了哦! 与 嗯!片刻。希望您能有所体验。
想象学校的自助餐厅。餐盘稀缺,但经理很聪明。他与他的员工一起制定了一项策略,目的是:及时为所有饥饿的学生提供食物,而不会因盘子不足而使任何人缺粮。
策略0: 他们计划在学生吃完饭并离开自助餐厅时收集所有用过的盘子进行洗涤,以使尚未吃过的盘子重新使用。
因此,每当服务团队报告餐盘用完时,都会派出专门的女服务员来收集所有用过的餐盘。只要正在使用盘子的学生离开自助餐厅,便会收集盘子。然后将这些盘子清洗并添加到堆中,以服务更多的学生。
该策略非常有效,经理先生对自己和他的员工感到满意。很快,他意识到一些学生吃完饭了,但仍坚持与朋友聊天和大笑。他曾告诉他的收藏家女服务员只在学生离开桌子时才收集盘子。结果,很多脏盘子被卡在桌子上,仅仅是因为满意的学生仍然坐在那里,而自助餐厅经常遇到盘子危机。
策略1:经理先生还另辟trick径。他对收集服务员的指示发生了变化:只要学生吃完饭,就收集一个盘子,无论他/她是否仍然坐在桌子上。学生们听到了新的和“敌对的”变化的消息,许多特殊群体都提出了与经理先生妥协的投诉。该策略被标记为失败。
策略2:作为一个聪明的人,他提出了更出色的想法,并为任何收集女服务生制定了一套指导方针:
如果学生是州长或行会代表,请允许他们将餐盘保留任意时间,直到他们明确要求您领取餐盘或离开餐桌为止。
如果学生是州长或行会代表的女友或男友,则让他们享有伴侣的特权……这意味着,即使他们已经吃完饭但仍在餐桌上,除非绝对没有,否则不要着急收拾盘子。自助餐厅的字面量已经达到了极限。
如果学生不是校长,并且不与任何校长有任何关系,就像大多数第一年级的情况一样,请对收集自己的盘子保持高度警惕-不要拖延,不要妥协。只要他们吃完饭,就算还是乞求哭泣,他们是否仍坐在桌子上也是如此。不允许他们拥有任何特权。
最后,医生已向糖尿病学生发送了一份清单,需要了解他们每天最后一餐的确切时间,以便确定何时进行常规血糖水平检查。就像您为上一个类别的学生一样,收集他们的盘子,但是在记下盘子并通知我时,请务必记下确切的时间。
事实证明,这是一个极好的策略。讨论迭代问题解决。
自助餐厅中的学生对Java对象的引用遵循一定的强度和特权层次。让我们深入探讨技术故障。
强大的参考
在每个Java程序中,对象都是保存和操作数据的方式:
StringBuilder sb = new StringBuilder();

在以上代码段中,新关键字创建了一个StringBuilder对象,并将对它的强引用存储在变量sb中。这是我们创建的所有对象的默认强度级别,因此我们不会像其余部分那样使用任何特殊标签来标识它们。
要在层次结构中创建其余的引用,我们需要驻留在java.lang.ref包中的特殊包装对象。
具有至少一个强引用的任何对象均不符合垃圾回收的条件。以我们的类比,知府对他们的盘子有很强的借鉴意义。用技术术语来说,我们说该对象是很容易达到的。
仅当我们取消其引用(类似于离开表格的主管)时,它才有资格进行收集:
sb = null;

软参考
可以通过将对象的实例包装在java.lang.ref.SoftReference对象中来创建对对象的软引用:
StringBuilder sb = new StringBuilder();
SoftReference 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 sbWeakRef = new WeakReference<>(sb);
sb = null;

在以上代码段中,在第三行中取消了强引用之后,该对象立即可以使用GC。
现在,该对象类似于没有特权的坐在一年级学生面前的旧盘子。可以立即收集它,而无需考虑学生是否仍坐在桌子旁。从技术上讲,我们说对象是弱可及的。
尽管我们仍然可以检索到对对象的强引用,但是机会窗口要比软引用小得多。与其他情况相比,我们得到空值的频率要高得多:
sb = sbWeakRef.get();

无论内存是否紧张,GC都会积极地收集只有弱引用的对象。
弱引用旨在用于规范化映射。仅仅5分钟的谷歌搜索就可以使您对该用例有一个了解,因此我不会赘述。
幻影参考
通过将对象的实例包装在java.lang.ref.PhantomReference对象中,可以创建对象的幻像引用:
StringBuilder sb = new StringBuilder();

ReferenceQueue refQ = new ReferenceQueue<>();

PhantomReference sbPhantomRef = new PhantomReference<>(sb, refQ);

sb = null;

在上面的代码段中,在第四行中取消了强引用之后,该对象立即可以使用GC。忽略ReferenceQueue对象,稍后再讲。现在,请记住,与软引用和弱引用不同,PhantomReference没有ReferenceQueue是没有用的。
现在,该对象类似于坐在一位患有糖尿病的一年级学生面前的旧盘子。即使有一个条件,也可以立即收集,而无需考虑学生是否仍然坐在桌子旁:记录了确切的收集时间并通知了文档。
只要已注明时间的纸还没有被医生使用和丢弃,我们将其称为幻影可到达的对象。(主要的困惑点,请继续阅读,但要当心)。
PhantomReference对象的get方法 是无用的,因为它始终返回null。相对于软和弱参考,这进一步增强了该参考对象的独特性。下一节将使这种独特性更加清晰。
虚拟引用旨在用作Object.finalize()方法的一种更灵活的替代 方法。
参考队列
顾名思义,引用队列是一种数据结构,它使引用对象入队:WeakReference,SoftReference和PhantomReference。
引用对象是否随时排队取决于我们在创建引用对象时是否提供ReferenceQueue对象。除了PhantomReference之外,提供一个不是强制性的,甚至不是有用的。
根据参考对象的类型,将其排队的确切点会有所不同。除了扩大幻象引用的范围外,我无意在此问题上做太多介绍。认真阅读下一段。
甲幻象参考排入只要对象是指已经完成由所述垃圾收集器。“最终化”表示已调用其finalize()方法。GC会在收集对象之前调用对象的finalize()方法,以使创建它们的应用程序受益。好处是有机会释放对象在其尘世生存期(读取Java堆寿命)中必须创建或使用的非“ GC’able” 资源。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块的finally子句中。想知道这是否是重复调用,因为您总是close()’ 'd您的文件资源?太好了,继续阅读!
现在注意方法主体顶部的if检查。如果您已经在代码内部调用close(),则上述代码中的fd将为null,并且方法主体的其余部分将不会执行。呵呵!!! (来不及了??)。继续阅读!
恰巧的是,这些天来,在敲定()方法,尤其是在库/平台类,如的FileInputStream上面,作为一个安全网,我们作为开发商是鲁莽和忘记释放非堆资源的情况。(我可能通过低声告诉您,您可能永远不必使用幻像ref,而请继续阅读)。
因此,所有这些与幻影引用和引用队列有什么关系。我失去焦点了吗?好吧,你问,所以我会回答:
在的finalize()方法是困扰与专柜几乎所有的优点,它试图提供许多问题。实际上,Joshua Bloch的《 Effective Java》一书对此进行了详细介绍,并且一些博客广泛地讨论了其问题。不要以为他们只是在弄脏本来出色的API的图像。除非您是即将上任的詹姆斯·高斯林(James Gosling),否则我强烈建议您只听从他们的话,并与其他选择相处融洽,以善用资源。现在,继续阅读-我们差不多完成了。
现在,我们可以使用本节的finalize()以及为什么引入幻影引用和引用队列。创建PhantomReference对象是为了“为finalize()提供更灵活的替代方法”。 无论成功与否是相当一个很大的话题,但我的选择是NO。
我们作为程序员的方式是通过在创建PhantomReference实例的同时提供ReferenceQueue实例,如我们之前所见。调用对象的finalize()方法后,其幻影ref就进入队列。我们有责任继续轮询参考队列以跟踪此事件的发生时间。在这里,我们手动释放资源,包括最终调用phantomRef.clear()方法以使基础引用无效。
差异汇总
我知道本节的标题有点la脚,但请多多包涵。我将尝试汇总三个参考对象的差异,以消除任何疑问:
入队:一旦GC根据其算法“标记”了它们的内存对象以进行收集,但尚未完成它们的确定,则将软参考对象和弱参考对象都加入队列。另一方面,一旦确定了内存对象,就将幻像引用加入队列。
Reference.get():软引用和弱引用的get方法都返回对基础对象的强引用;如果该对象已被标记为要收集,则返回null。另一方面,幻像引用的get方法始终返回null(无论您是Josh Bloch还是Jon Skeet都在调用它。这种差异触及了他们的用例的关键点,对于弱引用和软引用都可以用,一个人应该能够在“机会之窗”中为其对象创建强引用。但是幻影裁判并不需要像我们之前探索的那样。
Reference.clear():此方法在所有三个ref对象中可用。它将基础引用设置为null,因此显式地利用内存对象在其时间之前进行收集。对于弱引用和软引用,这是无用的功能,因为它会抵消它们的好处。但这是幻影引用的关键,因为您必须在清理步骤的最后将其称为幻影先生。
ReferenceQueue:对于软和弱引用,它是没有用的,但是没有它,幻影引用是没有用的。
用例:软引用:内存敏感的缓存,弱引用:规范化映射,幻像引用:更灵活的替代finalize()。
结论
在这篇文章中,尽管篇幅很长,但我试图讨论我关于GC与参考对象之间关系的发现。我希望它至少能使您更好地理解参考对象以及GC。
最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。在这里插入图片描述

发布了252 篇原创文章 · 获赞 20 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zhaozihao594/article/details/105184765
今日推荐