聊一聊Java中的垃圾回收算法、垃圾回收器、轻GC、重GC

(1)Java中的垃圾回收算法有哪些?

java中有四种垃圾回收算法,分别是标记清除法、标记压缩法、复制算法、分代收集算法。GC主要作用于堆内存。

标记清除法

第一步标记:利用可达性遍历内存,标记存活对象;

第二步清除:垃圾收集器会对堆内存再次遍历,清除掉所有未被标记的对象;

特点:①可能会产生内存碎片,因为在清除垃圾对象后,剩余的内存空间可能分散在堆内存的各个地方,不一定是连续的。程序运行时,需分配大对象,而找不到连续分片内存,就会触发一次GC。②效率比较低,采用递归遍历和全堆对象遍历。而且在GC的时候,需要停止应用,用户体验差。

标记压缩

第一步标记:利用可达性遍历内存,标记存活对象;

第二步压缩:将所有的存活的对象向一端移动,将端边界以外的对象都回收掉;

特点:适用于存活对象多,垃圾少的情况,适用于老年代的垃圾回收;无内存碎片产生,能有一块连续空闲的内存空间,内存利用效率高;

复制算法

将内存按照容量大小等分为两块,一块被称为“from”区,另一块被称为“to”区域。每次存储对象只使用from区,当from区使用完了,就进行一次Minor GC,就将还存活的对象移到to区,然后清除from区里面的所有对象。现在to区有一些存活对象,from区则为空。那么,现在身份对调,空的区域成为to区,包含存活对象的区域成为from区,接下来的对象存储,发生在新的from区。也就是说,to区在垃圾回收开始前和完成后都是空的。

特点:①不会产生内存碎片,但是内存使用率极低(50%);②每次垃圾回收时都需要进行大量的对象复制,效率取决于存活对象的比例。在Java中,年轻代区域,对象存活率低,通常采用复制算法。(年轻代包括 Eden 区、Survivor 0区和 Survivor 1区)

分代收集算法

根据内存对象的存活周期不同,java虚拟机将内存分成新生代和老生代,在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象的存活率极高,没有额外的空间对它进行存储,所以采用标记清除或者标记压缩算法。

(2)Java中常见的垃圾回收器有哪些?

CMS:是一种追求最短垃圾回收停顿时间为目标的收集器。高并发场景下,垃圾回收会造成应用长时间停顿,而CMS垃圾回收器解决了这个问题,它可以使得用户线程和 GC 线程并发执行。采用的是,标记清除算法,运作过程是初始标记,并发标记,重新标记(remark,并发标记时,线程可能重新引用不活跃对象,引用后就变成活跃对象),并发清除。

CMS特点:①垃圾收集结束会产生大量内存碎片;②产生浮动垃圾。并发清理阶段,用户线程还在继续运行,就还会伴随有新的垃圾对象不断产生,但新垃圾对象没有被标记过,CMS无法在当次收集中处理掉它们;③会造成CPU资源紧张,却会因为占用了一部分线程而导致应用程序变慢,程序吞吐量降低。如果CMS没有预留足够的内存空间提供给用户线程使用的话,还会造成并发失败、启用其它垃圾处理器、应用长时间停顿。

G1:Java虚拟机在JDK 9及之后中默认的垃圾回收器,尤其适用于大型服务器应用大内存场景。G1的目的是实现可预测的停顿时间更低的延迟,同时保持较高的吞吐量。G1是标记压缩算法实现,运作流程是初始标记,并发标记,最终标记(remark),筛选回收。

G1特点:①不会产生内存碎片,可以精确地控制停顿时间。②G1将整个堆分为大小相等的多个区域,G1跟踪每个区域的垃圾大小,在后台维护一个优先级列表,每次优先回收价值最大的区域(垃圾最多的区域),在有限时间里,尽可能拉高回收效率。

(3)Java中一次完整的GC是什么样的?

在 Java 中,内存被划分成两个不同的区域,新生代 ( Young ) 和老年代 ( Old ) ,新生代默认占总空间的1/3,老年代默认占2/3。新生代有3个分区,Eden区、Survivor 区(from区)、Survivor 区(to区),它们的默认占比是 8:1:1。新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。老年代的垃圾回收(又称Major GC),对象存活率高,通常使用标记清除或标记压缩算法。

GC流程

1. 对象优先分配到Eden区。当 eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。

①在Eden区执行了第一次GC之后,存活的对象会被移动到Survivor from区,Eden 区清空;

② Eden 区再次满了,进行第二次 GC,这时会采用复制算法,将 Eden 和 from 区一起清理,存活的对象会被复制到 to 区,并且from区和to区进行交换。

③移动一次,对象年龄加 1,对象年龄大于一定阀值会直接移动到老年代;

(GC年龄的阀值可以通过参数 -XX:MaxTenuringThreshold 设置,默认为 15;动态对象年龄判定,Survivor 区相同年龄所有对象大小的总和 > (Survivor 区内存大小 * 这个目标使用率)时,大于或等于该年龄的对象直接进入老年代。其中这个使用率通过 -XX:TargetSurvivorRatio 指定,默认为 50%)

④ Survivor to区,不足以容纳当前Eden区和from区中所有存活的对象,这时会出现Survivor区内存不足的情况。此时会发生担保分配超过指定大小的对象可以直接进入老年代

2. 大对象直接进入老年代,大对象需要大量连续内存空间存储。(比如,字符串、数组)(年轻代内存有限,大对象在年轻代的复制成本高,所以大对象直接进入老年代)

3. 老年代满了而无法容纳更多的对象,这时候会触发Full GC,Full GC 清理整个堆内存。(包括年轻代和老年代,存活的对象根据阈值或者大小来存放在幸存者区或者老年区)(若养老区执行了Full GC后发现依然无法进行对象的保存,就会产生OOM异常 “OutOfMemoryError ”,此时需要调整堆内存或者检查代码是否出现无限递归、死循环)(创建大量对象,并且无法被垃圾回收)

(4)Minor GC 和 Full GC 有什么不同呢?

空间:Minor GC发生在新生代。Full GC:发生在整个堆中,包括新生代、老年代,同时附加一个元空间。(存储类加载信息,原来是方法区)

Minor GC触发条件:Eden区满。

Full GC触发条件:①通过Minor GC后进入老年代的对象大小大于老年代的可用空间大小。②由Eden区、From 区向To区复制时,对象大小大于To 区可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。③调用System.gc时,程序员希望执行Full GC,但是jvm不一定执行,jvm有自主决定的权利。

  • 36
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值