JVM 调优总结之垃圾回收算法以及垃圾收集器

Java 的一大特点就是可以自动进行垃圾回收,不要开发人员去关心内存资源的释放情况。
本章主要记录垃圾回收方法和 Hot Spot 虚拟机支持的垃圾回收器。

垃圾回收要解决的问题
  • 那些对象需要被回收?
  • 何时回收这些对象?
  • 如何回收这些对象?
如何判断那些对象需要被回收

参考文章:JVM 如何判断一个对象可被回收?

垃圾回收算法与思想
  1. 标记-清除算法
    标记-清除将垃圾回收分为两个节点:标记阶段和清除阶段。在标记阶段,首先通过根节点(GC Root)标记从根节点开始的可达对象,未被标记的就是垃圾对象。在清除阶段将清除所有未被标记的对象。
    缺点: 空间碎片
    在这里插入图片描述
    如图所示,回收后的空间是不连续的。在对象的对空间分配过程中,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续空间的工作效率。

  2. 复制算法
    核心思想:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换内存的角色,完成垃圾回收。
    缺点: 内存减半
    在这里插入图片描述
    在 Java 的新生代串行垃圾回收器中,使用了复制算法的思想。新生代分为 eden 、s0 和 s1 3部分。其中 s0 、s1 可以视为用于复制的两块大小相同,地位相等,且可进行角色互换的空间块。s0 和 s1 用于存放未被回收的对象。
    复制算法比较使用与新生代。因为新生代垃圾对象通常多于存活对象,复制算法的效果比较好。
    在这里插入图片描述

  3. 标记-压缩算法
    复制算法适合在新生代中使用,但是在老年代中,大部分对象是存活对象。如果使用复制算法,由于存活的对象比较多,复制成本会很高。所以老年的垃圾回收需要使用新的算法。

    标记压缩算法是一种老年代的回收算法,其在标记-清除上做了一些优化。
    其在清除的过程中,将所有存活的对象压缩到内存的一端。然后清除边界外的所有空间,这样既避免了空间碎片,又不需要两块相同的内存空间。
    在这里插入图片描述

  4. 增量算法
    在垃圾回收的过程中,应用软件处于一种 Stop the World 的状态。应用程序的所有线程都会挂起,暂停正常工作,等待垃圾回收完成。
    基本思想: 让垃圾回收线程和应用线程交替执行。每次,垃圾回收线程只收集一小片区域的内存空间,接着切换到应用线程。如此反复,直到垃圾收集完成。
    但是,因为线程切换和上下文转化的消耗,会使垃圾回收的总成本上升,系统的吞吐量下降。

  5. 分代
    以 Hot Spot 虚拟机为例,年轻代使用复制算法,老年代使用标记-整理算法

垃圾收集器

按线程数分

  • 串行垃圾回收器
  • 并行垃圾回收器

按工作模式

  • 并发式垃圾回收器
  • 独占式垃圾回收器

按碎片处理方式

  • 压缩式垃圾回收器
  • 非压缩式垃圾回收器

按工作的内存空间

  • 新生代垃圾回收器
  • 老年代垃圾回收器

在这里插入图片描述

  1. 新生代串行收集器

    串行收集器主要有两个特点:
    - 它仅仅使用单线程进行垃圾回收
    - 它是独占式的垃圾回收
    在串行收集器进行垃圾回收时,Java 应用程序中的线程都要暂停,等待垃圾回收完成。
    在这里插入图片描述
    新生代串行处理器使用复制算法,实现简单,逻辑处理高效,并且没有线程切换的开销。
    在 Hot Spot 虚拟机中,使用 -XX:+UserSerialGC 参数指定使用新生代串行收集器和老年代串行收集器。在客户端(Client)模式运行时,它是默认的垃圾收集器。

  2. 老年代串行收集器

    老年代串行收集器使用的是标记-压缩算法。和新生代串行收集器一样,它也是串行、独占式的垃圾回收器。
    若要使用老年代串行收集器:

  • -XX:+UseSerialGC:新生代、老年代都使用串行收集器
  • -XX:+UseParNewGC:新生代使用并行收集器,老年代使用串行收集器(可以和 CMS 垃圾收集器一起使用)
  • -XX:+UseParallelGC:新生代使用并行回收回收器,老年代使用串行收集器(不可以和 CMS 垃圾回收器一起使用)
  1. 并行收集器
    只是简单的将串行回收器多线程化。
    在这里插入图片描述
  • -XX:+UseParNewGC:新生代使用并行收集器,老年代使用串行收集器
  • -XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用 CMS
    并行收集器工作时的线程数可以使用 -XX:ParallelGCThreads 指定。默认情况下,CPU 个数小于 8 时,其值等于 CPU 的数量;当 CPU 数量大于 8 时,其值为 3+[(5 * CPU_Count)/8]
  1. 新生代并行回收(Parallel Scavenge)收集器
    新生代并行回收收集器也是使用复制算法的收集器。表面来看,其和并行收集器一样,同样是多线程和独占式的垃圾回收。但是并行回收收集器的特点是: 其十分关注系统的吞吐量。
  • -XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器

  • -XX:+UseOldParallelGC:新生代和老年代都使用并行回收收集器
    并行回收收集器提供了两个重要的参数用于控制系统的吞吐量:
    (1)-XX:MaxGCPauseMillis:设置最大垃圾收集的停顿时间,它的值是大于0的一个整数。
    (2)-XX:GCTimeRatio:设置吞吐量大小,其值是 0~100 之间的整数。假设 GCTimeRatio 为 n,系统用于垃圾回收的时间不超过 1/(1+n)。默认 n=99,即不超过 1% 的时间。

    除了以上的不同,并行回收收集器和并行收集器的不同之处还在于,其支持自适应的 GC 调整策略。使用 -XX:+UseAdaptiveSizePolicy 可以打开自适应调整策略。
    在手工调优比较困难的场合下,可以直接使用这种自适应的方式,仅指定虚拟机的最大堆(-Xmx)、目标的吞吐量(GCTimeRatio)和停顿时间(MaxGCPauseMillis),让虚拟机自己完成调优工作。

  1. 老年代并行回收收集器

    老年代并行回收收集器使用标记-压缩算法,在 JDK 1.6 中才可以使用。
    在这里插入图片描述

    • -XX:+UseParallelOldGC
    • -XX:ParallelGCThreads
  2. CMS 收集器(Concurrent Mark Sweep)
    和并行回收收集器不同,CMS 收集器主要关注于系统的停顿时间。其使用的标记清除算法,同时也是一个使用多线程并行回收的垃圾收集器。
    CMS 主要步骤有:初始标记、并发标记、重新标记、并发清除和并发重置是可以和用户线程一起执行的。
    从整体上说,CMS 手机不是抢占式的。
    在这里插入图片描述
    CMS 默认启动的线程数是 (ParallelGCTheads + 3)/4,也可通过 -XX:ParallelCMSThreads 直接指定。
    当CPU资源比较紧张时,由于 CMS 收集器的线程的影响,应用系统的性能在垃圾回收阶段会很糟糕。

    回收阈值:-XX:CMSInitiatingOccupancyFraction,默认 68.
    CMS 是基于标记-清除算法的收集器,会造成大量的空间碎片,导致可用空间无法给大对象分配空间。
    CMS 提供了用于内存整理的参数:

    • -XX:+UseCMSCompactAtFullCollection(内存碎片的整理并不是并发执行的)
    • -XX:CMSFullFCsBeforeCompaction 用于设定进行多少次 CMS 后,进行一次内存压缩
  3. G1 收集器(Garbage First)
    G1 收集器基于标记-压缩算法的。

设置使用G1收集器

  • -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions

设置 G1 回收器的目标停顿时间

  • -XX:MaxGCPauseMillis=50
  • -XX:GCPauseIntervalMillis=200
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值