专栏首页程序猿DD一个超乎想象的垃圾收集器:ZGC

一个超乎想象的垃圾收集器:ZGC

来源:占小狼的博客

Z Garbage Collector,即ZGC,是一个可伸缩的、低延迟的垃圾收集器,主要为了满足如下目标进行设计:

  • 停顿时间不会超过10ms
  • 停顿时间不会随着堆的增大而增大(不管多大的堆都能保持在10ms以下)
  • 可支持几百M,甚至几T的堆大小(最大支持4T)

停顿时间在10ms以下,10ms其实是一个很保守的数据,在SPECjbb 2015基准测试,128G的大堆下最大停顿时间才1.68ms,远低于10ms,和G1算法相比,也感觉像是在虐菜。

G1算法通过只回收部分Region,避免了全堆扫描,改善了大堆下的停顿时间,但在普通大小的堆里却表现平平,ZGC为什么可以这么优秀,主要是因为以下几个特性。

Concurrent

ZGC只有短暂的STW,大部分的过程都是和应用线程并发执行,比如最耗时的并发标记和并发移动过程。

Region-based

ZGC中没有新生代和老年代的概念,只有一块一块的内存区域page,以page单位进行对象的分配和回收。

Compacting

每次进行GC时,都会对page进行压缩操作,所以完全避免了CMS算法中的碎片化问题。

NUMA-aware

现在多CPU插槽的服务器都是Numa架构,比如两颗CPU插槽(24核),64G内存的服务器,那其中一颗CPU上的12个核,访问从属于它的32G本地内存,要比访问另外32G远端内存要快得多。

ZGC默认支持NUMA架构,在创建对象时,根据当前线程在哪个CPU执行,优先在靠近这个CPU的内存进行分配,这样可以显著的提高性能,在SPEC JBB 2005 基准测试里获得40%的提升。

Using colored pointers

和以往的标记算法比较不同,CMS和G1会在对象的对象头进行标记,而ZGC是标记对象的指针。

其中低42位对象的地址,42-45位用来做指标标记。

Using load barriers

因为在标记和移动过程中,GC线程和应用线程是并发执行的,所以存在这种情况:对象A内部的引用所指的对象B在标记或者移动状态,为了保证应用线程拿到的B对象是对的,那么在读取B的指针时会经过一个 “load barriers” 读屏障,这个屏障可以保证在执行GC时,数据读取的正确性。

一些变化

JDK11

  • ZGC的最初版本
  • 不支持类卸载class unloading (使用 -XX:+ClassUnloading 没有效果)

JDK12

  • 进一步减少停顿时间
  • 支持类卸载功能

平台支持

ZGC目前只在Linux/x64上可用,如果有足够的需求,将来可能会增加对其他平台的支持。

对的,目前只支持64位的linux系统,狼哥在mac跑了半天都是下面的错!

如何编译

$ hg clone https://hg.openjdk.java.net/jdk/jdk$ cd jdk$ sh configure$ make images

如果正在编译的版本是 11.0.0, 11.0.1 or 11.0.2,必须加上配置参数 --with-jvm-features=zgc开启ZGC的编译,在11.0.3或者12之后,可以忽略这个参数,已经默认支持。

编译结束之后,你会得到一个完整的JDK。在Linux中,可以在下面目录中找到

./build/linux-x86_64-normal-server-release/images/jdk

可以进入bin文件夹,执行 ./java-version 验证一下。

如何使用

编译完成之后,已经迫不及待的想试试ZGC,需要配置以下JVM参数。

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx10g -Xlog:gc

参数说明:

Heap Size

通过 -Xmx10g进行设置。 -Xmx是ZGC收集器中最重要的调优选项,大大解决了程序员在JVM参数调优上的困扰。ZGC是一个并发收集器,必须要设置一个最大堆的大小,应用需要多大的堆,主要有下面几个考量:

  • 对象的分配速率,要保证在GC的时候,堆中有足够的内存分配新对象
  • 一般来说,给ZGC的内存越多越好,但是也不能浪费内存,所以要找到一个平衡。

Concurrent GC Threads

通过 -XX:ConcGCThread=4进行设置。 并发执行的GC线程数,如果没有设置,在JVM启动的时候会根据CPU的核数计算出一个合理的数量,默认是核数的12.5%,但是根据应用的特性,可以通过手动设置调整。

因为在并发标记和并发移动时,GC线程和应用线程是并发执行的,所以存在抢占CPU的情况,对于一些对延迟比较敏感的应用,这个并发线程数就不能设置的过大,不然会降低应用的吞吐量,并有可能增加应用的延迟,因为GC线程占用了太多的CPU,但是如果设置的太小,就有可能对象的分配速率比垃圾收集的速率来的大,最终导致应用线程停下来等GC线程完成垃圾收集,并释放内存。

一般来说,如果低延迟对应用程序很重要,那么不要这个值不要设置的过于大,理想情况下,系统的CPU利用率不应该超过70%。

Parallel GC Threads

通过 -XX:ParallelGCThreads=20当对GC Roots进行标记和移动时,需要进行STW,这个过程会使用ParallelGCThreads个GC线程进行并行执行。

ParallelGCThreads默认为CPU核数的60%,为什么可以这么大? 因为这个时候,应用线程已经完全停下来了,所以要用尽可能多的线程完成这部分任务,这样才能让STW尽可能的短暂。

总结

ZGC在实现上和以往的GC有很大的区别,后续会对ZGC的细节实现进行分析,对ZGC感兴趣的同学赶紧跟上,一起撸ZGC的源码。

号外:最近整理了之前编写的一系列内容做成了PDF,关注我并回复相应口令获取: - 001 领取:《Spring Boot基础教程》 - 002 领取:《Spring Cloud基础教程》 更多内容陆续奉上,敬请期待

本文分享自微信公众号 - 程序猿DD(didispace)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring Boot使用@Async实现异步调用:自定义线程池

    在之前的Spring Boot基础教程系列中,已经通过《Spring Boot中使用@Async实现异步调用》一文介绍过如何使用 @Async注解来实现异步调用...

    程序猿DD
  • 死磕Java并发:J.U.C之读写锁:ReentrantReadWriteLock

    重入锁ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少。然而读...

    程序猿DD
  • IntelliJ IDEA插件系列:五大装逼神器

    之前介绍了关于Intellij IDEA的插件和快捷键内容,非常受欢迎: 最好用的 IntelliJ 插件 Top 10 我最常用的Intellij IDEA...

    程序猿DD
  • java多线程加锁的简单处理办法 原

        当对数据修改时,如果两个线程同时去修改同一条数据,这样产生的结果就不是我们预期的结果。这时候就需要对修改操作进行加锁,让jvm里同一时刻只能有一个线程能...

    尚浩宇
  • Python项目开发之CMDB理解与分析

    ITIL就是IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库),由英国...

    菲宇
  • 浅谈ITIL

    TIL即IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库)由英国政府部...

    用户5760343
  • 使用交互组件(ipywidgets)“盘活”Jupyter Notebook(上)

    传统上,每次需要修改笔记本单元格的输出时,都需要更改代码并重新运行受影响的单元格。这可能很繁琐、低效甚至容易出错,对于非技术用户来说,甚至是不切实际的。这就是i...

    AiTechYun
  • xml解析技术概述和使用Jaxp对xml文档进行dom解析

    用dom和sax对xml文档进行解析,可以使用已开发的xml解析开发包,我们直接调用即可。xml解析开发包有:Jaxp(最差)、Jdom(一般)、dom4j(最...

    MonroeCode
  • 东软集团事业部总监樊建勋:大数据视角下的舆情监测与引导

    <数据猿导读> 东软集团事业部总监樊建勋在2016年中国通信大数据会上分享了以“大数据视角下的舆情监测与引导”为主题的演讲。他讲到,舆情这两个字在各行各业都倍加...

    数据猿
  • 《大话机器学习算法》决策树—实战项目

    如果你还不知道决策树算法,你可以选择和韩梅梅同学一起边相亲边学习决策树(手动狗头):

    知秋小一

扫码关注云+社区

领取腾讯云代金券