java的zgc的简单介绍
今天给各位分享java的zgc的知识,其中也会对进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
- 1、JVMGC及内存分配策略
- 2、将JVM从JDK11迁移到JDK16的问题
- 3、6个重要的JVM性能参数
- 4、上课要求jdk版本1.8.0而我的版本是jdk11,有什么区别
- 5、ZGC原理与实现分析
- 6、java12相对于java9来说都有哪些新特性
JVMGC及内存分配策略
GC就是垃圾回收,它的主要作用就是回收程序中不再使用的内存.
JVM执行GC内存回收的时候如何判断内存是否可以回收呢,就是看对象是否还存活,如果不存活则回收对象对一个的内存空间
那判断对象是否存活有哪些方法呢?
引用计数法顾名思义就是通过计算对象被引用的次数来判断对象是否还存活。
例如下面的代码片段
这种情况下可以理解为o p都是堆内存中的对象,另外p对象的 name 被 o 引用(这里 name字段也是一个对象)。如下
那么此时, o的引用计数为 1 , p的引用计数为2。也就是说引用计数法判断这个引用值为0的时候就会来回收这个对象所占用的内存。
这种方式的优缺点是什么呢
优点
缺点
java判断对象是否存活的依据就是可达性分析。
JVM根据一些GC Roots来进行可达性判断,若从某一个或某几个GC Root可以访问到这个对象那么这个对象就不可回收。即从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
那么GC Roots有哪些呢
一般我们创建对象的操作都是使用的强引用
一些有用但是并非必需,用软引用关联的对象,系统将要发生OOM之前,这些对象就会被回收。
虚拟机参数:
一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
幽灵引用,最弱,被垃圾回收的时候收到一个通知
这种处理方式但是容易出现内存的碎片导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要按顺序分配内存即可,实现简单,运行高效。这种算法的代价是将内存缩小为了原来的一半,造成内存的浪费。
当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor[1]。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存会被“浪费”。当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。
在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。
最古老的,单线程,独占式,成熟,适合单CPU 服务器
和Serial基本没区别,唯一的区别:多线程,多CPU的,停顿时间比Serial少
获取最短回收停顿时间为目标的收集器。
目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。
从名字(包含“Mark Sweep”)上就可以看出,CMS收集器是基于“标记—清除”算法实现的,它的运作过程相对于前面几种收集器来说更复杂一些,整个过程分为4个步骤,包括:
浮动垃圾:由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。同时用户的线程还在运行,需要给用户线程留下运行的内存空间。
特点:
G1当出现内存不足的的情况,也可能进行的FullGC回收。
GC收集器和我们GC调优的目标就是尽可能的减少Stop Thr World 的时间和次数。
ZGC通过技术手段把stw的情况控制在仅有一次,就是第一次的初始标记才会发生,这样也就不难理解为什么GC停顿时间不随着堆增大而上升了,再大我也是通过并发的时间去回收了
关键技术(这部分以后在找相关的知识补充)
将JVM从JDK11迁移到JDK16的问题
我们的后端网络服务运行在Java SE 11(JDK11)上。JDK11有很多现代化的功能,得到了Oracle和OpenJDK开发团队的长期支持,而且一直非常非常稳定,只有一个例外。内存尖峰管理。
我们有一个数据密集型的ETL进程,每天晚上运行。它从我们的数据科学团队的系统中把我们新计算出来的声誉分数加载到MongoDB集合中,以便于实时查询。一个Kubernetes实例对所有实例处理的工作进行排队,使ETL工作并行。我们的客户自然在同一时间安排了一些数据密集型的Reputation Score报告。这造成了内存激增,JDK11的垃圾收集器除了分配更多的内存外,无法处理。堆经常增长,超过了虚拟机上的可用内存,这导致Kubernetes回收排队的ELT作业的pod,这种数据损失随之而来。
Java SE 16(JDK16)默认启用了Z垃圾收集器(ZGC)。ZGC在根据需要释放内存方面做得更好,与应用程序并行工作,因此内存高峰得到了缓解。在JDK16下运行pod可以解决这个问题,我们的数据现在每天晚上都能成功加载。
现在我们已经看到了JDK16的强大功能,而且Java SE 17(JDK17)现在已经推出,并有一个长期的支持计划,我们决定将我们的代码库和构建系统迁移到JDK16,以利用它的许多好处。
JDK17,即下一个长期发布的JDK,已经发布。我们计划尽快迁移到JDK17。
下面是对我们在过渡期间遇到的问题的总结,以及未来升级的路径。
当时使用的框架
JDK11、Maven 3.6、Spring Boot 2.4.3、Groovy 2.5 和 3.7。
所需的框架版本
JDK16、Maven 3.8.1、Spring Boot 2.4.8、Groovy 当时不可用。
JDK 16 安装
操作系统
OpenJDK18 现在是 Homebrew 中的默认 OpenJDK。安装JDK16:
brew install openjdk@16
OpenJDK 是 Ubuntu 中的默认 JDK,可能是预安装的。
sudo apt install openjdk-16-jdk openjdk-16-source
如果安装了多个 JDK(可能是因为当前工作已经安装了 openjdk-11),请使用 update-alternatives 管理 JDK 版本:
原文:将JVM从JDK11迁移到JDK16的问题 - reputation
6个重要的JVM性能参数
围绕垃圾收集和内存,您可以将600多个参数传递给 JVM 。如果包括其他方面,则JVM参数总数将很容易超过1000+。任何人都无法消化和理解太多的论据。在本文中,重点介绍了七个重要的 JVM 参数,在 Java性能测试 中起着非常重要的作用。
-Xmx 可能是最重要的 JVM 参数。 -Xmx 定义要分配给应用程序的最大堆大小。。您可以这样定义应用程序的堆大小: -Xmx2g 。
堆大小在影响应用性能和所需物理硬件需求。这带来了一个问题,我的应用程序正确的堆大小是多少?我应该为应用程序分配大堆大小还是小堆大小?答案是:取决于需求和预算。
将 -Xms 和 -Xmx 设置为相同值的会提高JVM性能
元空间是将存储 JVM 的元数据定义(例如类定义,方法定义)的区域。默认情况下,可用于存储此元数据信息的内存量是无限的(即受您的容器或计算机的RAM大小的限制)。您需要使用 -XX:MaxMetaspaceSize 参数来指定可用于存储元数据信息的内存量的上限。
-XX:MaxMetaspaceSize=256m
OpenJDK中有7种不同的GC算法:
如果您未明确指定GC算法,那么JVM将选择默认算法。在Java 8之前, Parallel GC 是默认的GC算法。从Java 9开始, G1 GC 是默认的GC算法。
GC算法的选择对于确定应用程序的性能起着至关重要的作用。根据我们的研究,我们正在使用Z GC算法观察到出色的性能结果。如果使用 JVM 11+ ,则可以考虑使用 Z GC 算法(即 -XX:+ UseZGC )。
下表总结了激活每种垃圾收集算法所需传递的JVM参数。
垃圾收集日志包含有关垃圾收集事件,回收的内存,暂停时间段等信息,可以通过传递以下JVM参数来启用垃圾收集日志:
从JDK 1到JDK 8:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:{file-path}
从JDK 9及更高版本开始:
-Xlog:gc*:file={file-path}
Demo:
通常,GC日志用于调整垃圾回收性能。但是,GC日志包含重要的微观指标。这些指标可用于预测应用程序的可用性和性能特征。在本文中将重点介绍一种这样的标尺:GC吞吐量。GC吞吐量是您的应用程序在处理客户交易中花费的时间与它在处理GC活动中花费的时间之比。假设您的应用程序的GC吞吐量为98%,则意味着应用程序将其98%的时间用于处理客户活动,其余2%用于GC活动。
现在,让我们看一个健康的JVM的堆使用情况图:
您会看到一个完美的锯齿图案。您会注意到,当运行Full GC(红色三角形)时,内存利用率将一直下降到最低。
现在,让我们看一下有问题的JVM的堆使用情况图:
您可以注意到,在图表的右端,即使GC反复运行,内存利用率也没有下降。这是一个典型的内存泄漏迹象,表明该应用程序正在存在某种内存问题。
如果您仔细观察一下该图,您会发现重复的完整GC开始在上午8点左右开始。但是,该应用程序仅在上午8:45左右开始获取OutOfMemoryError。到上午8点,该应用程序的GC吞吐量约为99%。但是,在上午8点之后,GC吞吐量开始下降到60%。因为当重复的GC运行时,该应用程序将不会处理任何客户交易,而只会进行GC活动。
OutOfMemoryError 是一个严重的问题,它将影响您的应用程序的可用性和性能。要诊断 OutOfMemoryError 或任何与内存相关的问题,必须在应用程序开始遇到 OutOfMemoryError 的那一刻或一瞬间捕获堆转储。由于我们不知道何时会抛出 OutOfMemoryError ,因此很难在抛出时左右的正确时间手动捕获堆转储。但是,可以通过传递以下JVM参数来自动化捕获堆转储:
-XX:+ HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath = {HEAP-DUMP-FILE-PATH}
在 -XX:HeapDumpPath 中,需要指定堆转储所在的文件路径。传递这两个JVM参数时,将在抛出 OutOfMemoryError 时自动捕获堆转储并将其写入定义的文件路径。例:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof
一旦捕获了堆转储,就可以使用 HeapHero 和 EclipseMAT 之类的工具来分析堆转储。
每个应用程序将具有数十,数百,数千个线程。每个线程都有自己的堆栈。在每个线程的堆栈中,存储以下信息:
他们每个都消耗内存。如果它们的使用量超出某个限制,则会引发 StackOverflowError 。可以通过传递-Xss参数来增加线程的堆栈大小限制。例:
-Xss256k
如果将此 -Xss 值设置为一个很大的数字,则内存将被阻塞并浪费。假设您将 -Xss 值指定为 2mb ,而只需要 256kb ,那么您将浪费大量的内存。
假设您的应用程序有500个进程,然后 -Xss 值为 2Mb ,则您的线程将消耗 1000Mb 的内存。另一方面,如果您仅将 -Xss 分配为 256kb ,那么您的线程将仅消耗 125Mb 的内存。每个JVM将节省 875Mb 内存。
注意:线程是在堆(即 -Xmx )之外创建的,因此这 1000Mb 将是您已经分配的-Xmx值的补充。
现代应用程序使用多种协议(即SOAP,REST,HTTP,HTTPS,JDBC,RMI)与远程应用程序连接。有时远程应用程序可能需要很长时间才能做出响应,有时它可能根本不响应。
如果没有正确的超时设置,并且远程应用程序的响应速度不够快,则您的应用程序线程/资源将被卡住。远程应用程序无响应可能会影响您的应用程序的可用性。它可以使您的应用程序停止磨削。为了保护应用程序的高可用性,应配置适当的超时设置。
您可以在JVM级别传递这两个强大的超时网络属性,这些属性可以全局适用于所有使用 java.net.URLConnection 的协议处理程序:
sun.net.client.defaultConnectTimeout :指定建立到主机的连接的超时(以毫秒为单位)。例如,对于HTTP连接,它是与HTTP服务器建立连接时的超时。当建立与资源的连接时, sun.net.client.defaultReadTimeout 指定从输入流读取时的超时(以毫秒为单位)。
例如,如果您要将这些属性设置为2秒:
注意,默认情况下,这两个属性的值为-1,这表示未设置超时。
上课要求jdk版本1.8.0而我的版本是jdk11,有什么区别
jdk版本迭代都是根据上一代进行增添新功能。djk11在1.8版本上只是添加了少许新内容以适应现在互联网du技术节奏,除了新添加的内容,两者没有什么影响。也就是,如果不用到新添加的内容,运行不受影响。但是需要知道,有哪些内容是新的。
JDK1.8的新特性:
一、接口的默认方法Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法。
二、Lambda 表达式在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:
Collections.sort(names, (String a, String b) - {return b.compareTo(a);});
三、函数式接口Lambda表达式是如何在java的类型系统中表示的,每一个lambda表达式都对应一个类型,通常是接口类型。
而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法不算抽象方法,所以也可以函数式接口添加默认方法。
四、方法与构造函数引用Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:
converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);
五、Lambda 作用域在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。
六、访问局部变量可以直接在lambda表达式中访问外层的局部变量:
七、访问对象字段与静态变量 和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:
八、访问接口的默认方法JDK 1.8 API包含了很多内建的函数式接口,在老Java中常用到的比如Comparator或者Runnable接口,这些接口都增加了@FunctionalInterface注解以便能用在lambda上。
Java 8 API同样还提供了很多全新的函数式接口来让工作更加方便,有一些接口是来自Google Guava库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。
扩展资料:
jdk11新特性:
1、字符串加强
// 判断字符串是否为空白" ".isBlank(); // true// 去除首尾空格" Javastack ".strip(); // "Javastack"// 去除尾部空格 " Javastack ".stripTrailing()。
// 去除首部空格 " Javastack ".stripLeading(); // "Javastack "// 复制字符串"Java".repeat(3); // "JavaJavaJava"// 行数统计"A\nB\nC".lines().count(); // 3
2、HttClient Api
这是 Java 9 开始引入的一个处理 HTTP 请求的的孵化 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在java.net包中找到这个 Api
3、用于 Lambda 参数的局部变量语法
用于 Lambda 参数的局部变量语法简单来说就是支持类型推导:
var x = new A();for (var x : xs) { ... }try (var x = ...) { ... } catch ...
4、ZGC
从JDK 9开始,JDK使用G1作为默认的垃圾回收器。G1可以说是GC的一个里程碑,G1之前的GC回收,还是基于固定的内存区域,而G1采用了一种“细粒度”的内存管理策略,不在固定的区分内存区域属于surviors、eden、old。
而我们不需要再去对于年轻代使用一种回收策略,老年代使用一种回收策略,取而代之的是一种整体的内存回收策略。
这种回收策略在我们当下cpu、内存、服务规模都越来越大的情况下提供了更好的表现,而这一代ZGC更是有了突破性的进步。
从原理上来理解,ZGC可以看做是G1之上更细粒度的内存管理策略。由于内存的不断分配回收会产生大量的内存碎片空间,因此需要整理策略防止内存空间碎片化。
在整理期间需要将对于内存引用的线程逻辑暂停,这个过程被称为"Stop the world"。只有当整理完成后,线程逻辑才可以继续运行。
ZGC原理与实现分析
支持TB级堆内存(最大4T)
最大GC停顿10ms
对吞吐量影响最大不超过15%
SPECjbb 2015基准测试,128G堆内存,单次GC停顿最大1.68ms, 平均1.09ms
在对象的引用中借用几个bit存储额外状态标记,Load Barrier会根据这些状态标记执行不同的逻辑
加载屏障:在应用线程从堆中加载对象应用后,执行的一段逻辑
跟CPU中的内存屏障(Memory barrier)完全没有关联
目前ZGC没有分代,每次GC都会标记整个堆
将堆分为 2M(small), 32M(medium), n*2M(large)三种大小的页面(Page)来管理,根据对象的大小来判断在那种页面分配
在relocation阶段将Page中活的对象转移到另一个Page,并整个回收原Page。会根据一定算法选择部分Page进行整理。
大部分对象标记和对象转移都是可以和应用线程并发。只会在以下阶段会发生stop-the-world
1. GC开始时对root set的标记时
2. 在标记结束的时候,由于并发的原因,需要确认所有对象已完成遍历,需要进行暂停
3. 在relocate root-set 中的对象时
逻辑上一次ZGC分为Mark(标记)、Relocate(迁移)、Remap(重映射)三个阶段
Mark: 所有活的对象都被记录在对应Page的Livemap(活对象表,bitmap实现)中,以及对象的Reference(引用)都改成已标记(Marked0或Marked1)状态
Relocate: 根据页面中活对象占用的大小选出的一组Page,将其中中的活对象都复制到新的Page, 并在额外的forward table(转移表)中记录对象原地址和新地址对应关系
Remap: 所有Relocated的活对象的引用都重新指向了新的正确的地址
实现上,由于想要将所有引用都修正过来需要跟Mark阶段一样遍历整个对象图,所以这次的Remap会与下一次的Remark阶段合并。
所以在GC的实现上是2个阶段,即MarkRemap阶段和Relocate阶段
向下箭头表示STW, 横向箭表示并发阶段
在64位系统中,ZGC利用了对象引用的4bit( 低42位: 对象的实际地址)
Marked0/marked1: 判断对象是否已标记
Remapped: 判断应用是否已指向新的地址
Finalizable: 判断对象是否只能被Finalizer访问(本文分析忽略此标记)
这几个bits在不同的状态也就代表这个引用的不同颜色
为什么有2个mark标记?
每一个GC周期开始时,会交换使用的标记位,使上次GC周期中修正的已标记状态失效,所有引用都变成未标记。
GC周期1:使用mark0, 则周期结束所有引用mark标记都会成为01。
GC周期2:使用mark1, 则期待的mark标记10,所有引用都能被重新标记。
通过Linux系统调用mmap将标记位(001,010,100)三种地址空间映射到同一地址上,使三种地址解析后都指向同一地址,Load Barrer保证返回的地址是其中一个。
GC在每个阶段维护一个全局的唯一的期望标记,当发现引用的状态跟期望的不一致,Load barrier会修复应用的标记到期待的状态。并会根据状态的不同执行不同的逻辑。
下面分析不同阶段的实现流程
期待的标记值为001,此处只关注Mark操作,Remap逻辑下面说明。
当前加载的引用标记010,Load barrier会将引用的标记修正为001,然后保存回这个引用的来源对象中,这样在下次再加载相同时可以避免重复执行。同时会帮助GC进行对象标记,方式为将这个引用添加到当前线程的本地标记stack中,并发的GC线程会遍历这些引用,并递归遍历引用的对象图
期待的标记值为100
GC线程会执行为relocation set执行relocate工作,将page编辑为relocating(迁移中),只迁移对象,不关注对象的引用,relocation结束后,对象的引用会指向过期的位置。
此阶段业务线程加载对象引用时,进行remap操作:先判断指向的页面状态是否为relocating, 如果是relocating, 会协助GC线程做relocate工作。并更新此引用的的标记为100,如果不是relocating,直接更新标记为100。
当Relocation阶段完成时会存在部分引用未更新,标记为001。
来到下一次GC周期:
期待的标记值为010
如果当前加载的引用为100,表示已完成remap,更新标记为010
如果为其他状态,则会执行rmap操作,然后更新标记为010
同时会对对象进行mark操作,前面已经说明。
如此反复切换。
我们知道在一些GC算法下分配对象是通过撞指针法,也即是TLAB机制来分配。在ZGC中针对不同类型的Page,有不同的分配机制。
在堆上分配对象时,是根据对象的大小选择在不同类型的Page中分配,不同Page对象的分配策略不同。
Small Page(=256K): 每个CPU会关联一个small page,线程在分配对象时,先查找线程所运行在的cpu id, 找到关联的Page,进行分配。page剩余内存不够时,会尝试在新Page分配并切换cpu绑定的page为新的page。
Medium Page(=4M): 所有线程在同一个Page分配
Large Page: 每个large对象占用一个Page, 根据对象大小先分配合适大小的Page,然后在Page中分配对象
ZGC目前有4中机制触发GC
1. 定时触发, 默认为不使用,可通过ZCollectionInterval参数配置
2. 预热触发, 最多三次,在堆内存达到10%、20%、30%时触发,主要时统计GC时间,为其他GC机制使用
3. 分配速率, 基于正态分布统计,计算内存99.9%可能的最大分配速率,以及此速率下内存将要耗尽的时间点,在耗尽之前触发GC(耗尽时间 - 一次GC最大持续时间 - 一次GC检测周期时间)
4. 主动触发, (默认开启,可通过ZProactive参数配置) 距上次GC堆内存增长10%,或超过5分钟时,对比距上次GC的间隔时间跟(49 * 一次GC的最大持续时间),超过则触发
第一次STW, 标记roots对象
并发标记阶段,所有活对象以及对象引用都被标记
此后会有第二次STW,确保所有对象都被标记
选择需要整理的Page集合(relocation set)
第三次STW, 转移root中的对象
当一个Page内的活对象全部转移后,此Page的内存可以立即重用。
这是个和有用的特性,relocation set中下个page的对象可以转移到这个释放的内存中,理论上在GC时只需要有一个可转移的空页就可以了。
到此,一个GC周期就结束了。
剩下的修复工作由Load Barrier以及下次GC来完成
java12相对于java9来说都有哪些新特性
随着互联网的不断发展,软件编程开发语言也有了多次的更新与升级,而今天电脑培训就通过案例分析来了解和学习一下,java12都有哪些新特性。SwitchExpressions或者说起相关的PatternMatching特性,为我们提供了勾勒出了Java语法进化的一个趋势,将开发者从复杂繁琐的低层次抽象中逐渐解放出来,以更高层次更优雅的抽象,既降低代码量,又避免意外编程错误的出现,进而提高代码质量和开发效率。
则是很有现实意义度ShenandoahGC。它是Redhat主导开发的PauselessGC实现,从大概2013年开始研发,终于取得了重要的阶段性成果,与其他PauselessGC类似,ShenandoahGC主要目标是99.9%的暂停小于10ms,暂停与堆大小无关等。
也许了解ShenandoahGC的人比较少,业界声音比较响亮的是Oracle在JDK11中开源出来的ZGC,或者商业版本的AzulC4(ContinuouslyConcurrentCompactingCollector)。但是,笔者认为,至少目前,其实际意义大于后两者,因为:
使用ZGC的低门槛是升级到JDK11,对很多团队来说,这种版本的跳跃并不是非常低成本的事情,更何况是尚不清楚ZGC在自身业务场景中的实际表现如何。
而C4,毕竟是土豪们的选择,现实情况是,有多少公司连个几十块钱的License都不舍得
而ShenandoahGC可是有稳定的JDK8u版本发布的哦,据我所知已经有个别公司在HBase等高实时性产品中实践许久。
关于java的zgc和的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。