电话

028-126520321

JVM的分代垃圾回收机制和垃圾回收算法是指Java虚拟机的内存管理和自动内存回收系统。首先,让我们来了解一下什么是GC。GC (Garbage Collection) 垃圾回收的意思是系统会自动回收不再使用的内存,以提高内存利用率。

标签: 2024-06-26 

在C/C++编程中,当需要使用内存时,通常需要手动进行申请分配,等使用完毕后再手动释放。这两个步骤极为繁琐,且经常容易出现相关问题。在Java中,所有这些都会被自动处理,所以我们编写代码时无需再关注这些无效数据。目前流行的虚拟机通常是基于分代收集理论进行设计的。由于在虚拟机中,大部分对象都是生命短暂的,经过多次垃圾回收的对象会变得难以被清理。因此,堆中的数据结构被分为两个部分,即新生代和老年代。新生代主要存储短命对象,而老年代则存放长寿对象。

​ 1、 Minor GC/Young GC 是指只对新生代进行垃圾回收。

2、老年代垃圾回收(Major GC/Old GC)是指只对老年代进行回收的过程。目前只有 CMS 回收器会执行这种独立回收老年代的操作。Major GC 的定义存在一些混淆,一些人认为它指老年代,而另一些人认为它指整个堆的收集。这取决于具体情境,没有一个固定的说法。整堆回收 (Full GC) 指的是收集整个 Java 堆和方法区(需要注意方法区也包含在内)。垃圾回收算法中的复制算法(Copying)则是将一块内存区域平均分成两部分,当其中一部分内存被使用完时,存活的对象会被移到另一部分内存区域,原先的内存区域将被回收。不需要考虑内存碎片区域,只需按顺序分配内存即可。简单实现,高效运行。然而,这种方法也存在一个缺点,即仅有50%的内存利用率。为解决这个问题,在JVM中出现了以下解决方案:Appel式回收,向Eden区添加内存。通常,内存区域的分配比例为:Eden区80%,Survivor区20%(From 10%,To 10%)。当Survivor区空间不足时,老年代必须进行内存分配担保。

2、标记-清理算法(Mark-Sweep)

​分为“标记”和“清理”两个步骤:首先,需要对所有可回收对象进行标记,接着对被标记对象进行清理,效率稍低。由于需要大量标记和清除操作,回收效率不如复制算法高。如果大部分对象都是短命的,那么需要标记的对象就会更多,回收效率会受到影响。其另一个重要问题是会产生许多内存碎片,导致大型对象无法被存储,因此必须提前触发其他垃圾回收。标记-整理法和清除法的步骤类似,但它的第二步是整理标记之外的所有对象,将它们向前移动后直接清除掉它们占据的内存空间。使用标记法不会导致内存碎片,但效率相对较低。整理法和清除法的主要区别在于整理法是对对象进行整理,而清除法是对对象进行清除;而移动对象则需要暂停所有的业务线程,然后更新所有对象的引用(直接指针需要进行调整)。四、JVM的垃圾收集器

 

1、Serial/Serial Old

​ JVM最初采用的垃圾回收器,单线程,独占式,适用于单CPU系统。对于内存堆大小在几十兆到几百兆之间的情况,它是适用的。然而,若超出此范围,则回收效率会显著降低。因此,对于目前而言,这一功能并不实用。停止世界(STW)是在进行垃圾回收时,必须暂停所有工作线程,直到回收完成。这种暂停被称为“停止世界”,但STW导致了糟糕的用户体验,比如每经过一小时应用就要停止响应5分钟。这也是早期JVM和Java被指责性能差的一个重要因素。因此,JVM 的开发团队一直在努力消除或减少停顿时间。自JDK 1.3起开始,JVM采用了多线程的垃圾回收器,并专注于吞吐量,以提高JVM的回收效率。这种垃圾回收器可以更有效地利用CPU时间,以加快完成程序的运算任务。CPU的吞吐量是指CPU用于执行用户代码的时间与总消耗时间的比例,即吞吐量=执行用户代码时间/(执行用户代码时间+垃圾收集时间),在虚拟机总共运行了100分钟的情况下,垃圾收集占用了1分钟,此时吞吐量为99%。适合回收堆空间中几十兆到几个GB 的内存。JVM参数配置 JDK1.8的默认设置如下 -XX:+UseParallelGC:新生代使用Parallel Scavenge,老年代使用Parallel Old -XX:MaxGCPauseMillis 但切勿误以为通过减小此参数值可以加快系统的垃圾收集速度,缩短垃圾收集停顿时间是以降低吞吐量和新生代空间为代价的:系统缩小新生代,收集300MB新生代肯定比收集500MB要快,然而这也造成了垃圾收集更加频繁的情况,之前每10秒进行一次收集,每次停顿100毫秒,现在变成每5秒进行一次收集,每次停顿70毫秒。虽然停顿时间在缩短,但吞吐量也有所减少。虽然暂停时间减少了,但处理能力也有所下降。

-XX:GCTimeRatio

-XX:GCTimeRatio 参数的数值应为一个介于 0 到 100 的整数,表示垃圾回收时间占总时间的比例,即处理能力的倒数。

例如:如果将这个参数设为 19,那么允许最大垃圾回收时间占总时间的 5%(即 1/(1+19)),默认值是 99,即允许最大 1% 的垃圾回收时间[即 1/(1+99)]。由于与吞吐量有密切关系,ParallelScavenge 被称为“吞吐量优先垃圾回收器”。使用自适应大小策略 (-XX:+UseAdaptiveSizePolicy) 默认是开启的。这是一个开关选项,一旦启用,就不需要手动指定新生代大小(-Xmn)、Eden区和Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象的大小(-XX:PretenureSizeThreshold)等详细参数。虚拟机将根据系统当前的运行状况收集性能监控信息,动态调整这些参数,以实现最合适的停顿时间或最大的吞吐量。

 

3、ParNew/CMS

ParNew

​与Parallel相似的垃圾回收器,唯一不同的地方在于:它是多线程、多 CPU 的,因此停顿时间比Serial要短。在JDK9之后,将ParNew整合到CMS中了。   CMS回收器致力于实现尽可能短的垃圾回收停顿时间(STW)。当前许多 Java 应用都集中在互联网或者基于浏览器的服务端,这些应用非常重视服务的响应速度,希望降低停顿时间以提升用户体验。使用“Mark Sweep”这个名字,可以知道这个垃圾回收器是采用标记-清除算法。它的操作步骤相较之前的其他几个回收器更为复杂。整个过程分为4个步骤:首先是初始标记阶段,只标记与GC根节点直接关联的对象,这些对象相对较少,标记速度较快。对于并发标记,是指标记与初始化标记的对象相关联的所有对象,在这类对象比较多时采用并发处理,与用户线程同时执行。对

重新标记:修复在并发标记过程中出现标记异常的对象标记。这一步骤比初始标记略长一点,但比并发标记要快得多。并发清理:与用户线程同时运行,执行对象回收。

-XX:+UseConcMarkSweepGC 表示新生代将使用ParNew,而老年代将使用CMS。

不足之处:

​ CPU敏感:由于采用并发技术,对处理器核心的要求较高。在进行CMS并发清除时,由于采用了轻量级的并发方式,所以在清理过程中会产生新的垃圾。

​ 所以在执行回收时,需要留出一定空间来存放产生的垃圾(JDK 1.6 设定的阈值为92%)。如果用户线程生成的垃圾太多,导致内存不足时,会引发Concurrent Mode Failure,此时虚拟机会暂时启用Serial Old代替CMS。由于使用标记-清除算法,会导致内存碎片的产生。

的亮点:总的来说,由于 CMS 是 JVM 的第一个并发垃圾收集器,因此仍然具有代表性。为何选择标记-清除方法?因为在执行 CMS 时,如果还要整理对象,就需要暂停业务线程,进行对象整理会导致 STW 时间更长。出于追求 STW 时间的考量,没有选择标记-整理。然而,CMS 的主要问题在于采用了标记清除算法,这会导致内存碎片问题。当内存碎片较多时,大对象的分配会变得困难。为解决此问题,CMS 提供了一种解决方法,即参数:-XX:+UseCMSCompactAtFullCollection。通常情况下这个参数都会被开启。若无法分配大对象,则会启动内存碎片整理的过程。通常会优先使用 Serial Old 这个选项,因为它是单线程的。因此,如果内存空间很大且对象很多,使用 CMS 时可能会出现卡顿现象。垃圾回收器适合回收大约20至30GB的堆空间。Garbage First (G1) 垃圾收集器的设计理念与之前所有其他垃圾回收器都不同。 以往的垃圾回收器通常采用代(Generation)划分的方式设计,而 G1 将堆视为一个整体区域,将其划分为大小相同的独立区域(Region),每个区域在需要时可以充当伊甸园(Eden)、幸存者(Survivor)或老年代(Old Generation)区域。在执行对象回收时,可以根据各个区域的情况进行回收,以提高效率。

区域

上面提到每个区域都可以担当不同的角色,还有一个类似于老年代的 Humongous 区域,专门用来存放大对象。若某对象的大小超过了Region区空间的一半,则被视为大对象。可以利用参数-XX:G1HeapRegionSize来设置每个区域的大小,可选取范围为1MB到32MB之间的值。N 的平方。 对于超过整个区域容量的大型对象,它们将被存放在N个连续的巨大区域中。在大多数情况下,G1回收器将把这些巨大区域视为老年代的一部分。使用参数:-XX:+UseG1GC 开启 G1 垃圾回收器。

堆区分区大小设置为:-XX:+G1HeapRegionSize

 

 

 

 

一般建议逐渐增加此数值,随着 size 的增加,垃圾的存活时间也更长,GC 的间隔也会更长,但每次 GC 的执行时间也会更长。

最大GC暂停时间:-XX:MaxGCPauseMillis

 

执行阶段

 

G1的运行可以大致分为以下四个步骤:

初始标记(Initial Marking):标记与GC根相关的对象,修改TAMS指针的值,在此过程中需要暂停用户线程,但持续时间非常短BOB半岛入口。在进行下一步骤和并发标记时,用户线程会创建新的对象,这些对象被认为是存活对象而不是垃圾。现在就需要划分一个小区域来存放这些物品。并发标记 (Concurrent Marking):是指在进行扫描时标记所有待回收的对象。在扫描结束后,会产生引用变化的对象,并且这些对象可能会被误标记,SATB算法可以解决这些误标记的对象。在SATB(快照于开始时)中,类似于快照,在保存当前区域的同时,会在最终标记阶段进行比对,查看漏标的部分将重新进行标记(后续文章将详细解释)。

最终标记 (Final Marking):暂停所有用户线程,对之前未标记的对象进行标记。筛选回收(实时数据统计与清除):更新区域的数据统计,为每个区域的回收价值进行排序,根据用户设定的等待时间制定回收计划,并可以自由选择任意数量的区域进行回收。将要回收的区域复制到空白的区域中,然后清除原始的整个区域。由于涉及到对象移动,需要暂停所有用户线程,以便多个回收器线程可以进行回收工作。

特点:

​ 并行与并发:G1能够\n充分利用多CPU、多核环境下的硬件优势,利用多个CPU(CPU或CPU核心)来减少Stop-The-World停顿时间。一些其他垃圾收集器

需要暂停Java线程执行GC动作,但G1收集器可以通过并发方式使Java程序可以继续执行。

​ 分代收集:与其他收集器相同,G1 仍然保留了分代概念。尽管 G1 可以独立管理整个 GC 堆而无需其他收集器的辅助,但它可以通过不同的方式对新创建的对象和已经存在一段时间、经历过多次 GC 的旧对象进行处理,以获得更出色的收集效果。

空间整合:不同于 CMS 的“标记-清理”算法,G1 的整体设计基于“标记-整理”算法,在局部(两个Region之间)的设计则是基于“复制”算法,但无论如何,这两种算法都意味着在 G1 运行期间不会出现内存碎片,收集后会提供整齐的可用内存。这一属性有助于使程序长时间运行,当分配大对象时不会因为无法找到连续的内存空间而导致提前触发下一次垃圾回收。通过设置 -XX:MaxGCPauseMillis 参数来追求最大停顿时间的目标,G1垃圾收集器会调整新生代和老年代的比例、堆大小以及晋升年龄,以实现该目标。这种垃圾回收器适合回收数百 GB 的堆空间。如果选择在 G1 垃圾收集器和 CMS 垃圾收集器之间,平衡点会在 6 到 8G 左右,只有在内存较大的情况下,G1 才能更好地发挥其优势。

BOB半岛首页

JVM的分代垃圾回收机制和垃圾回收算法是指Java虚拟机的内存管理和自动内存回收系统。首先,让我们来了解一下什么是GC。GC (Garbage Collection) 垃圾回收的意思是系统会自动回收不再使用的内存,以提高内存利用率。

BOB半岛下载

BOB半岛入口


BOB半岛入口 BOB半岛官方

推荐新闻