详细的讲一遍Java GC

本文从Java的垃圾定义、如何回收垃圾、垃圾回收策略等方面详细讲一遍Java的垃圾回收机制

什么是垃圾回收

垃圾回收(Garbage Collection,GC),就是释放垃圾占用的存储空间,对内存中(主要是堆)已经死亡或长时间未被使用的对象进行清除和回收,防止内存泄漏。

内存模型

先看下jdk 1.6、1.7、1.8的内存模型

jdk1.6

jdk1.7

jdk1.8

可以看到,jdk从1.6到1.8的演进中,将常量池放到了堆里面,将永久代移出了JVM内存,放到了机器内存中(元空间-metaspace)。初衷在于将不可回收或很难回收的放到了堆外内存(永久代的回收有非常严格的要求),可控的收拢在堆内。

如何定义垃圾

GC前,我们得直到哪些内存是可以回收的。这里就涉及到垃圾回收的相关算法:引用计数算法和可达性分析算法

引用计数法

引用计数法是在对象中分配一块空间来记录该对象被引用的次数。如果该对象被其他对象引用一次则+1,如果删除对该对象的引用,则引用次数-1。当该对象的引用次数为0时,那么该对象就可被回收。

String str = new String("west");
str = null;

创建一个字符串,则字符串"west"有1个引用,即 str;将 str 置为 null,则 "west" 的引用次数为0。在引用计数算法中,"west"所占用的内存即可被回收了(写代码中,不用的对象显示置为null即是这个道理)。

从上面也可以看到,引用计数法是将垃圾判断逻辑放到了应用的运行当中,而不是在垃圾回收时,停止整个应用的运行(stop the world),直到对堆中的垃圾回收处理完毕。但引用计数法被淘汰也有它的弊端,当我们定义2个对象(计数+1),且这两个对象相互引用(计数+1),再置空2个对象的声明引用(计数-1)时,实际这2个对象已经不可能在被访问了,但因为计数器不为0(1+1-1=1),GC回收器永远无法回收他们。

可达性分析算法

可达性分析算法是通过 GC ROOT 作为起点,从这些节点向下搜索的路径,当一个对象在这个路径上都没有被任何引用时,则证明该对象是未被使用(不可达)可回收的。

可以看到,可达性算法解决了引用计数算法里的“循环依赖”问题,只要对象和GC ROOT没有关联(直接或间接),则可被回收。

上文说的不用的对象显示置为 null 即没有被引用,即可被及时清理。

什么是GC ROOT

看上面内存模型图,GC ROOT 的对象包含:虚拟机栈中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中的Native方法引用的对象。

如何回收垃圾

在确定了哪些是垃圾可以回收后,GC要做的就是如何回收这些垃圾。不同的虚拟机有不同的方式来实现GC,常见的有标记清除算法、标记整理算法、复制算法、分代收集算法,下面我们详细介绍下各个算法的实现原理。

标记清除算法

上图可以很明显的看出来,标记清除算法分为2步:标记出要回收的区域,然后清除这些空间内的垃圾。被清理出来的区域就变为未使用的内存区域。

不足:标记清除会产生大量的内存碎片,可用的内存空间会被切割成多块,而创建对象做内存分配时,需要的是连续的内存空间。如上图所示,如果现在需要一块4M的内存区域,其实是无法开辟出来的,但实际我们可用的内存空间总量是大于4M的。也就是我虽然有足够的内存空间,实际却不可用。

标记整理算法

如上图,标记整理算法将所有存活的对象向一端移动,再清理掉边界外的内存区域。这样避免了内存碎片问题,未使用的内存空间都是持续的空间。

不足:标记整理算法对内存做了频繁的变动,需要整理所有存活对象的引用地址并指向新的内存地址,性能和效率差很多。

复制算法

如上图,复制算法是将内存区域分为两块同等大小的区域,每次只使用其中一块。当这一块内存用完了,将存活着的对象迁移到另一块未使用的区域,再将原有的区域的内存空间全部清理掉。复制算法解决了内存碎片问题,逻辑简单,性能高效。

不足:总共14M的内存空间,实际可用的只有7M

分代收集算法

分代收集算法针对对象存活周期的不同,将堆内存分为新生代和老年代,并针对这两块区域的特点采用最合适的收集算法。可以认为分代收集算法是上述几种算法的组合。

在新生代中,每次GC时都有绝大部分对象死亡,只有极少量存活,则可以使用复制算法,只需要小量的复制存活对象成本就可完成垃圾收集。老年代中,因为存活的对象较多较大,没有多余的空间来做复制,一般使用标记整理算法来回收。

先看下分代收集的内存模型:

新生代

所有new出来的对象都会放在 Eden 区,当 Eden 区空间不足时,JVM发起 Minor GC,Eden 区中未被回收的存活对象移入 S0,如 S0 空间不足时,则再次 Minor GC,将存活的对象移入 S1,将S0的对象清除(复制算法),并记录移入对象被GC的次数。下一次 Minor GC 时,S0 和 S1的职责互换,以此反复。直到存活对象被GC的次数超过15时,才会被移入老年代。

可以认为,S0,S1就是老年代的一个缓存,且在缓存中的对象有极大可能在被GC15次之前被回收。而两个 Survivor区(S0,S1),又可以保证总有一个区域是空闲的,而另一个是没有碎片可使用的。据说不多分几个 Survivor,是经过权衡后最佳的选择。过多可能导致单个 Survivor 区域过小,容易被塞满。

老年代

老年代占据了堆内存中大部分的空间,只有在Major GC的时候才会进行清理,但每次GC时会"stop-the-world",即其他线程都会被暂停。

需要注意,大对象需要连续的大量内存空间,因此被创建出来时就会直接进到老年代。避免在新生代中做大量的内存复制操作。

常用垃圾收集器

一张图总结:Serial、Parallel Old、CMS、G1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值