Java废品回收器对循环引用对象的处理机制 (java废弃方法)
循环引用的定义和问题
循环引用是指两个或多个对象之间形成了相互引用的关系,形成了一个环状结构。例如,对象 A 引用了对象 B,而对象 B 又引用了对象 A,它们之间形成了一个循环引用。在这种情况下,如果没有采取措施,这些对象将无法被废品回收器正确地释放,导致内存泄漏和资源浪费的问题。废品回收器的标记-清除算法和根可达性分析
废品回收器使用标记-清除算法来处理循环引用。该算法通过根可达性分析来判断对象是否可达。根可达性分析从一组根对象开始,遍历对象图,将所有可达的对象标记为存活对象,未被标记的对象即为待回收对象。废品回收器对待回收对象进行清理操作,释放其所占用的内存。弱引用和幽灵引用
为了解决循环引用导致的内存泄漏问题,Java 引入了弱引用和幽灵引用两种特殊的引用类型。 弱引用 (WeakReference):当一个对象仅被弱引用指向时,即使该对象还存在其他引用,废品回收器也会将其回收。弱引用通常用于缓存数据,当内存不足时可以自动回收缓存对象,避免内存溢出。 幽灵引用 (PhantomReference):幽灵引用是最弱的引用类型,它无法直接获取到引用对象。幽灵引用通常与引用队列 (ReferenceQueue) 结合使用,用于在对象被废品回收前进行一些清理操作。Java 废品回收器处理循环引用的策略
在处理循环引用时,Java 废品回收器采用了以下策略: 根可达性分析:通过根可达性分析,废品回收器可以找到所有可达的对象,将其标记为存活对象,并回收未被标记的对象。 弱引用回收:当一个对象仅被弱引用指向时,即使该对象还存在其他引用,废品回收器也会将其回收。 幽灵引用清理:幽灵引用通常与引用队列结合使用,在对象被废品回收前进行一些清理操作。 重复标记与清除:对于循环引用中的对象,废品回收器会进行多次标记与清除操作,直到没有可达的对象为止。优化循环引用处理的策略
为了提高循环引用处理的效率和性能,Java 废品回收器采用了一些优化策略: 分代收集:将堆内存划分为不同的代,根据对象的生命周期采用不同的废品回收策略。通过这种方式,可以更加高效地处理循环引用问题。 并发标记与清除:对于大规模的对象图,废品回收器可以采用并发标记与清除的方式,减少停顿时间,提高系统的响应能力。 增量式收集:将废品回收过程分为多个阶段,每个阶段只处理一部分对象。通过增量式收集,可以将废品回收的时间分散到多个小的时间片段,减少对系统的影响。最佳实践和注意事项
开发人员应遵循最佳实践和注意事项,避免不必要的循环引用,合理使用引用类型,及时释放资源,并定期进行性能测试和分析,以确保程序的稳定性和高效性。通过正确处理循环引用,可以充分利用 Java 的自动内存管理机制,提高应用程序的性能和用户体验。「JVM基础」——废品回收基础(GC相关)
当内存中的某一个对象无法找到任何引用的时候,这个对象就是一个废品对象。
内存泄露(memory leak),是指程序中已动态分配的堆内存由于某种原因程序未将其释放或无法释放,造成了内存的浪费,导致程序运行速度减慢甚至程序崩溃等严重后果。
STW即stop the world ,指的是JVM进行GC时会暂停所有业务线程。
给每一个对象添加一个引用计数器,t每当有新的引用时,计数器+1,引用结束后计数器-1。任何时刻计数器为0的对象都是不被引用的。
优点: 1. 引用计数算法在回收废品时具有实时性。当一个对象的引用为0的时候会被直接回收,无需等待特定时间就可以释放内存。 缺点: 1.当出现对象之间循环引用的时候,废品回收期无法确定这些对象是否是废品,因此无法回收循环引用的对象。(内存泄漏)
从GC Roots节点(起始节点)出发向下搜索,如果没有任何引用链(既GC root不可达),则证明此对象不可用
tracing GC的本质是通过找出所有活对象来把其余空间认定为“无用”,而不是找出所有死掉的对象并回收它们占用的空间。GC roots这组引用是tracing GC的起点。要实现语义正确的tracing GC,就必须要能完整枚举出所有的GC roots,否则就可能会漏扫描应该存活的对象,导致GC错误回收了这些被漏扫的活对象。
将需要清除的对象标记出来,清除掉。
标记清除算法的实现分为两个阶段:
优点: 只对存活的对象进行标记。标记完毕后再扫描整个空间中未被标记的对象进行回收。该算法不需要进行对象的移动,只需对不存活的对象进行处理,效率高。 缺点 因为直接回收掉了不存活对象,未对内存进行整理,因此会产生内存碎片。内存碎片较多时,当大对象进入内存空间,无法为期分配足够的内存会提前触发GC。
将内存一分为二,每次使用一个区域。当触发gc时,将存活对象复制到另一区域,清除原区域。
它开始时把堆分成 一个对象 面和多个空闲面, 程序从对象面为对象分配空间,当对象满了,基于copying算法的废品 收集就从根集合(GC Roots)中扫描活动对象,并将每个 活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。
优点 :
缺点:
标记清除算法的优化实现,清除废品对象的同时压缩空间。
该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后再清理掉无用对象
优点: 1.因为对空间进行了整理,因此不会产生内存碎片。 缺点 1.因为扫描了两次,并且在清除的基础上还增加了整理,因此时间成本高。
两个互相引用对象的废品回收
给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1。 当引用失效时,计数器值就减1。 任何时刻计数器为0的对象就是不可能再被使用的。 但是主流的java虚拟机没有采用引用计数算法,其中最主要的原因就是它很难解决对象之间互相循环引用的问题。 例子: 对象A和B互相引用,但除此之外,这两个对象再无任何引用,但是他们因为互相引用着对方,所以导致他们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收他们。
主流的实现中,都是通过可达性分析来判定对象是否存活。 算法的基本思想:通过一系列的被称为“gc roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到“gc roots”没有任何引用链相连时,则证明此对象是不可用的。 可以作为“gc roots”的对象 (1)虚拟机栈(栈针中的局部变量表)中引用的对象 (2)方法区中类静态属性引用的对象。 (3)方法区中常量引用的对象 (4)本地方法栈中JNI引用的对象。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。