深入浅出 JVM GC(2)

# 前言

深入浅出 JVM GC(1) 中,限于上篇文章的篇幅,我们留下了一个问题 : 如何回收? 这篇文章将重点讲述这个问题。

在上篇文章中,我们也列出了一些大纲,今天我们就按照那个大纲来逐个讲解。在此,我将大纲复制过来。

垃圾回收算法

  1. 标记清除算法
  2. 复制算法
  3. 标记整理算法
  4. 分代收集算法(堆如何分代)

有哪些垃圾收集器

  1. Serial 串行收集器(只适用于堆内存256m 以下的 JVM )
  2. ParNew 并行收集器(Serial 收集器的多线程版本)
  3. Parallel Scavenge (PS 收集器,该收集器以吞吐量为主要目的,是1.8的默认 GC)
  4. CMS 收集器(该收集器全称 Concurrent Mark Sweep,是一种关注最短停顿时间的垃圾收集器)
  5. G1 收集器(JDK 9 的默认 GC)

有哪些GC

  1. Young GC(又称 YGC,minor GC,年轻代 GC)
  2. Old GC (老年代 GC,只有 CMS 才会单独回收 Old 区)
  3. Full GC(又称 major GC)
  4. Mixed GC(混合 GC,G1 收集器独有)

1. 有哪些垃圾回收算法

  1. 标记清除算法
  2. 复制算法
  3. 标记整理算法
  4. 分代收集算法(堆如何分代)
1. 标记清除算法

GC 中最基础的算法就是标记-清除算法,所谓标记清除,就是通过可达性分析,标记哪些是垃圾对象,然后清除。之所以说是最简单的算法,是因为后面的几种算法都是基于它的。

但是这个算法有2个不足之处:1. 碎片问题,标记清除之后会导致大量内存不连续的碎片,空间碎片太多会导致分配大对象时无法找到足够的连续内存从而提前触发 Full GC (此 GC 严重影响应用性能)。2. 效率问题,标记和清除这两个过程的效率都不高。我们通过一幅图来看看标记清除的算法:

可以从上图看出,回收后,出现了大量的内存不连续的内存块。

2. 复制算法

为了解决效率问题,人们发明了一种复制算法(Coping)。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的用完了,就开始垃圾回收,将有用的对象复制到另一个空闲的内存上,清空之前使用的内存块。这样使得每次都对整个半区回收,而且也不用考虑内存碎片问题,只需要移动堆顶指针,按顺序分配即可,实现简单,运行高效。

但是凡事都是有缺点的,复制算法的缺点就是内存缩小到了原来的一半,无法充分利用内存空间。

总是有取舍的。

现代的所有商业虚拟机都是采用这种算法来回收新生代。基于统计学,人们得出99% 的对象都是朝生夕死的,所以不需要留出那么大的空间保存存活的对象,也就是不要1:1 的比例来划分内存。

通常的做法是:

将内存分为一个较大的Eden(伊甸园)空间和两块较小的 Survivor(幸存区)空间,每次使用 Eden 和其中一块 Survivor ,当回收时,将 Eden 和 Survivor 还存活着的对象一次性的复制到另外一块 Survivor 空间,最后清理掉 Eden 和刚刚使用的 Survivor 空间。Hotspot 默认的比例是 8:1:1,也就是说,每次新生代可用内存空间为新生代总空间的90%,只有10%的内存会被浪费,从一定程度上解决了复制算法浪费空间的问题。

当然,98% 的对象可回收只是一般的情况下,我们无法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够用时怎么办呢?肯定需要依赖其他内存(老年代)进行所谓的分配担保(Handle Promotion)。

什么是分配担保呢?

如果另外一块 Survivor 区域没有足够空间存放上一次新生代手机下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。当然,具体细节这句话无法详细说明,我们将会在之后阐述具体细节。

3. 标记整理算法

从上面我们可以看出,复制算法的效率很高,请注意,该算法只有在对象存活率较低的时候(98% 对象可被回收)才能体现出效率。而如果一次 GC 活动之后,存活对象很多,那么就需要复制大量的对象,很明显,会导致效率不高;更关键的是,还需要额外的空间进行分配担保

所以,存活对象时间很长的老年代一般不使用该算法。

根据老年代的特点,一般使用“标记-整理(Mark-Compact)”算法,标记过程仍然与 “标记清除” 算法一样,但我们知道,标记清除算法会产生大量的内存碎片,对性能影响很大,所以标记整理算法后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象像一个方向移动,然后清理掉边界之外的内存。也就是将那些原来散落的对象移动在一起,让碎片不再存在。

可以说,标记整理算法相对于标记清除算法牺牲了一些性能,但却避免了内存碎片的产生,在大部分场合,可抵消掉整理过程中产生的性能损耗。

4. 分代收集算法

上面我们提到了几个名词,新生代,老年代,这些就是分代算法中名词。分代算法最主要的就是根据对象存活周期的不同将内存分成几块,一般是把 Java 堆分成新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

在新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记清理” 或者 “标记整理” 算法来进行回收。

2. 有哪些垃圾收集器

上面说的这些算法都是实现垃圾收集器的基础。

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

Hotspot 虚拟机所包含的所有收集器如下图:

从上图中看到,一共有6种 GC 组合(忽略 G1 和 为CMS备份的 SerialOld 组合 )。

  1. Serial + Serial Old
  2. Serial + CMS
  3. ParNew + CMS
  4. ParNew + Serial Old
  5. Parallel Scavenge + Serial Old
  6. Parallel Scavenge + Parallel Old

大家看到这里,一定有个疑问,为什么需要这么多垃圾收集器?

答案是:没有任何一种垃圾收集器是完美的,没有任何一种垃圾收集器适合所有的应用情况。

每个应用都需要自己的特定垃圾收集器,因此,可以说,GC 调优是门艺术,没有放之四海皆准的 GC。需要工程师们去根据应用的特性不断调优。

这么多 GC ,限于篇幅,我们将在 深入浅出 JVM GC(3) 中慢慢解释。
这里只是列出一个大纲。

接下来我们将说说关于 GC 的一些概念,方便阅读后面的关于 GC 处理器的文章。

3. 有哪些GC

  1. Young GC(又称 YGC,minor GC,年轻代 GC)
  2. Old GC (老年代 GC,只有 CMS 才会单独回收 Old 区)
  3. Full GC(又称 major GC)
  4. Mixed GC(混合 GC,G1 收集器独有)

关于这些 GC 的分类,R 大一个回答比较清楚:Major GC和Full GC的区别是什么?触发条件呢?

从大的方面讲,GC 只分为两种,一种是不收集整个堆,一种是收集整个堆。

Partial GC:并不收集整个GC堆的模式

  1. Young GC:只收集young gen的GC
  2. Old GC:只收集old gen的GC。只有CMS的concurrent collection是这个模式
  3. Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式

Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。

1. YGC

YGC 又称 Young GC ,minor GC ,年轻代 GC。顾名思义,该 GC 过程发生在年轻代中。从分代算法中,我们知道,JVM 为了性能考虑,通常将内存区域根据对象生命周期的不同分为年轻代和年老代。

新创建的对象基本上都存放在年轻代(除了一些大对象),因为大多数对象都是很快变成引用不可达,所以大多数对象都在年轻代中创建,然后消失。当对象从这块内存区域消失时,我们称之为 YGC。

什么时候发生 YGC 呢?当 Eden 不够放入新创建的对象时,也就是Eden 区满了,JVM 就会清理Eden 区的空间,将存活的对象放入 to 区,如果 to 区放不下,则直接进入老年代。如果 to 区能放下,则放入 to 区,然后清理掉无用对象,第二次 YGC 时,GC 扫描 Eden 区和 to 区,将这两个区的存活对象放入到 from 区,将 to 区清空(总之一定会保证有一个 Survivor 区是干净的),同样的,如果 from 区放不下,则通过分配担保机制进入老年代。如果 YGC 后,仍放不下新对象,则也通过分配担保进入老年代。

2. Old GC

通常,我们将 Old GC 等同于 Full GC,为什么呢?我们详细解释一下。

什么时候发生 Old GC? 当老年代空间满了的时候。也就是说通常是 YGC 后有很多对象进入到老年代,而老年代无法放下这些对象,这时候就需要对老年代 GC。而通常的 Old GC 其实就是 Full GC 。

3. Full GC

也就是全 GC ,对整个堆和方法区(如果存在)进行 GC。
哪些情况会 Full GC 呢?

  1. System.gc() 方法的调用
  2. heap dump 带 GC
  3. 永久代(方法区)空间不够
  4. 当准备出发 YGC 时,发现之前 YGC 后晋升对象的大小比目前 Old 区的剩余空间大,则不会触发 YGC ,转而直接触发 Full GC。

第四条说到晋升,什么是晋升呢?YGC 后,幸存的对象会放入到 Survivor 区,如果一个对象在多次 YGC 后仍然存活,则进入老年代,这个过程叫做晋升。每次 YGC 后,这个对象的年龄加一。当然,晋升的条件比较复杂。我们后面会详细讲述。

4. Mixed GC

G1 专属GC,这里不准备讲述这个 GC。

总结

到这里,我们解释了3种垃圾回收算法,第四个不算是算法,而是一种设计。还大致讲了5种收集器,并将这个坑留在了后面的文章里,最后讲了一些 GC 术语,YGC ,Old GC ,Full GC 等。

好了,关于5种垃圾收集器的详细介绍,我们将在 深入浅出 JVM GC(3)中详细说明。

good luck!!!

原文地址:https://www.cnblogs.com/stateis0/p/9062188.html

时间: 2024-10-23 20:30:27

深入浅出 JVM GC(2)的相关文章

深入浅出 JVM GC(3)

# 前言 在 深入浅出 JVM GC(2) 中,我们介绍了一些 GC 算法,GC 名词,同时也留下了一个问题,就是每个 GC 收集器的具体作用.有哪些 GC 收集器呢? Serial 串行收集器(只适用于堆内存 256M 以下的 JVM ) ParNew 并行收集器(Serial 收集器的多线程版本) Parallel Scavenge (PS 收集器,该收集器以吞吐量为主要目的,是1.8的默认 GC) CMS 收集器(该收集器全称 Concurrent Mark Sweep,是一种关注最短停顿

深入浅出 JVM GC(1)

# 前言 初级 Java 程序员步入中级程序员的有一个无法绕过的阶段------GC(Garbage Collection).作为 Java 程序员,说实话,很幸福,不用像 C 程序员那样,时刻关心着内存,就像网上有句名言------生活从来都不容易,只不过是有人替你负重前行!是的,GC 在替我们做这些脏活累活,GC 像让我们把精力都放在业务上,而不用每时每刻都在想着内存.现在,GC 也是每个语言的标准配置了.不然谁会去使用这个语言呢? 然而,作为一个合格的程序员,对底层的好奇是进步的动力,如果

深入浅出 JVM GC(4)常用 GC 参数介绍

# 前言 从前面的3篇文章中,我们分析了5个垃圾收集器,还有一些 GC 的算法,那么,在 GC 调优中,我们肯定会先判断哪里出现的问题,然后再根据出现的问题进行调优,而调优的手段就是 JVM 提供给我们的那些参数或者说选项,这些参数将会改变 GC 的运行方式.因此,他们显得极为重要. 我们将每一个垃圾收集器相关的参数一个一个娓娓道来,注意,楼主推荐一个小程序:前阿里 JVM 大神寒泉子的公众号里面有个小程序------JVM Pocket,这个小程序介绍了所有的 JVM 参数的作用,你可以在里面

Java性能优化之JVM GC(垃圾回收机制)

Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行.当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成.GC优化很多时候就是减少stop-the-world 的发生. JVM GC回收哪个区域内的垃圾? 需要注意的是,JV

JVM GC调优一则--增大Eden Space提高性能

缘起 线上有Tomcat升级到7.0.52版,然后有应用的JVM FullGC变频繁,在高峰期socket连接数,Cpu使用率都暴增. 思路 思路是Tomcat本身的代码应该是没有问题的,有问题的可能是应用代码升级,或者环境改变了,总之Tomcat的优先级排在最后. 先把应用的heap dump下来分析下: jmap -dump:format=b,file=path pid 用IBM的Heap Analyser分析,发现dubbo rpc调用的RpcInvocation对象和taglibs的Si

【甘道夫】HBase随机宕机事件处理 & JVM GC回顾

一.引言 本文记录了困扰团队两周的HBase随机宕机事件的解决方案,并回顾了JVM GC调优基础知识,供各位参考. 欢迎转载,请注明出处: http://blog.csdn.net/u010967382/article/details/42394031 二.实验环境 16台虚拟机,每台4G内存,1核CPU,400G硬盘 Ubuntu 14.04 LTS (GNU/Linux 3.13.0-29-generic x86_64) CDH5.2.0套装(包括相应版本的Hadoop,HIVE,Hbase

【转载】Java性能优化之JVM GC(垃圾回收机制)

章来源:https://zhuanlan.zhihu.com/p/25539690 Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行.当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成.GC优化很多时候就是减少stop-

JVM GC算法 垃圾回收器

JVM的垃圾回收算法有三种: 1.标记-清除(mark-sweep):啥都不说,直接上图 2.标记-整理(mark-compact) 3.复制(copy) 分代收集算法                                                    目前的垃圾回收都采用分代收集算法.也就衍生了很多垃圾收集器 "分代收集"(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法. 在新生

JVM GC总结

一:Java内存区的简单介绍 1.堆(Heap) JVM初始分配的内存由-Xms指定,默认是物理内存的1/64. JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4. 默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=参数,来指定. 默认空余堆内存小于70%时,JVM会减少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=参数,来指定. 注:服务器一般设置-Xms和-Xmx相等以避免每次GC后频繁的