后端面试每日一题 垃圾回收算法(3)

本文详细解释了Java中的垃圾收集机制,包括可达性分析如何判断对象死亡,以及标记-清除、标记-复制和标记-整理三种垃圾回收算法的特点与问题。同时讨论了GCRoots的作用和对象死亡状态的判断过程,以及finalize()方法在内存管理中的角色。
摘要由CSDN通过智能技术生成

}

即使 one 和 two 都为 null,但因为循环引用的问题,两个对象都不能被垃圾收集器所回收。

可达性分析算法(Reachability Analysis) 是目前商业系统中所采用的判断对象死亡的常用算法,它是指从对象的起点(GC Roots)开始向下搜索,如果对象到 GC Roots 没有任何引用链相连时,也就是说此对象到 GC Roots 不可达时,则表示此对象可以被垃圾回收器所回收,如下图所示:

img

当确定了对象的状态之后(存活还是死亡)接下来就是进行垃圾回收了,垃圾回收的常见算法有以下几个:

  • 标记-清除算法;

  • 标记-复制算法;

  • 标记-整理算法。

标记-清除(Mark-Sweep)算法属于最早的垃圾回收算法,它是由标记阶段和清除阶段构成的。标记阶段会给所有的存活对象做上标记,而清除阶段会把没有被标记的死亡对象进行回收。而标记的判断方法就是前面讲的引用计数算法和可达性分析算法。

标记-清除算法的执行流程如下图所示:

img

从上图可以看出,标记-清除算法有一个最大的问题就是会产生内存空间的碎片化问题,也就是说标记-清除算法执行完成之后会产生大量的不连续内存,这样当程序需要分配一个大对象时,因为没有足够的连续内存而导致需要提前触发一次垃圾回收动作。

标记-复制算法是标记-清除算法的一个升级,使用它可以有效地解决内存碎片化的问题。它是指将内存分为大小相同的两块区域,每次只使用其中的一块区域,这样在进行垃圾回收时就可以直接将存活的东西复制到新的内存上,然后再把另一块内存全部清理掉。这样就不会产生内存碎片的问题了,其执行流程如下图所示:

img

标记-复制的算法虽然可以解决内存碎片的问题,但同时也带来了新的问题。因为需要将内存分为大小相同的两块内存,那么内存的实际可用量其实只有原来的一半,这样此算法导致了内存的可用率大幅降低了。

标记-整理算法的诞生晚于标记-清除算法和标记-复制算法,它也是由两个阶段组成的:标记阶段和整理阶段。其中标记阶段和标记-清除算法的标记阶段一样,不同的是后面的一个阶段,标记-整理算法的后一个阶段不是直接对内存进行清除,而是把所有存活的对象移动到内存的一端,然后把另一端的所有死亡对象全部清除,执行流程图如下图所示:

img

GC Roots

在 Java 中可以作为 GC Roots 的对象,主要包含以下几个:

  • 所有被同步锁持有的对象,比如被 synchronize 持有的对象;

  • 字符串常量池里的引用(String Table);

  • 类型为引用类型的静态变量;

  • 虚拟机栈中引用对象;

  • 本地方法栈中的引用对象。

死亡对象判断

当使用可达性分析判断一个对象不可达时,并不会直接标识这个对象为死亡状态,而是先将它标记为“待死亡”状态再进行一次校验。校验的内容就是此对象是否重写了 finalize() 方法,如果该对象重写了 finalize() 方法,那么这个对象将会被存入到 F-Queue 队列中,等待 JVM 的 Finalizer 线程去执行重写的 finalize() 方法,在这个方法中如果此对象将自己赋值给某个类变量时,则表示此对象已经被引用了。因此不能被标识为死亡状态,其他情况则会被标识为死亡状态。

以上流程对应的示例代码如下:

public class FinalizeTest {

// 需要状态判断的对象

public static FinalizeTest Hook = null;

@Override

protected void finalize() throws Throwable {

super.finalize();

System.out.println(“执行了 finalize 方法”);

FinalizeTest.Hook = this;

}

public static void main(String[] args) throws InterruptedException {

Hook = new FinalizeTest();

// 卸载对象,第一次执行 finalize()

Hook = null;

System.gc();

Thread.sleep(500); // 等待 finalize() 执行

if (Hook != null) {

System.out.println(“存活状态”);

} else {

System.out.println(“死亡状态”);

}

// 卸载对象,与上一次代码完全相同

Hook = null;

System.gc();

Thread.sleep(500); // 等待 finalize() 执行

if (Hook != null) {

System.out.println(“存活状态”);

} else {

System.out.println(“死亡状态”);

}

}

}

上述代码的执行结果为:

执行了 finalize 方法

存活状态

死亡状态

从结果可以看出,卸载了两次对象,第一次执行了 finalize() 方法,成功地把自己从待死亡状态拉了回来;而第二次同样的代码却没有执行 finalize() 方法,从而被确认为了死亡状态,这是因为任何对象的 finalize() 方法都只会被系统调用一次。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

写在最后

很多人感叹“学习无用”,实际上之所以产生无用论,是因为自己想要的与自己所学的匹配不上,这也就意味着自己学得远远不够。无论是学习还是工作,都应该有主动性,所以如果拥有大厂梦,那么就要自己努力去实现它。

最后祝愿各位身体健康,顺利拿到心仪的offer!

由于文章的篇幅有限,所以这次的蚂蚁金服和京东面试题答案整理在了PDF文档里

蚂蚁、京东Java岗4面:原理+索引+底层+分布式+优化等,已拿offer

蚂蚁、京东Java岗4面:原理+索引+底层+分布式+优化等,已拿offer

蚂蚁、京东Java岗4面:原理+索引+底层+分布式+优化等,已拿offer
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
335)]

[外链图片转存中…(img-1TgWVTYY-1713584475335)]

[外链图片转存中…(img-FNdP4XU8-1713584475336)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值