Java最新技术介绍和分析 (202305)

news2024/9/25 5:21:38

说明:本文完成了2023年5月份,当时最新的LTS版本是Java17,本文在撰写时参考了美团技术团队和阿里JDK团队相关的文章,以及本文也引了用文章中的图片。在此表示感谢!

Java版本火车

相信老牌的Java开发者和爱好者把Java的版本停留在经典的java5,Java 6,以及Java8和Java11版本上,这几个版本都是java的里程碑版本。 Java 社区决定从Java 9开始每半年发布一个新版本,同时LTS(long-term support)版本从原来的每3年改为每2年发布一个。下图是我根据Oracle官网Support Roadmap做的甘特图。

Java 7,8,11. 17以及还未发布的Java 21均是LTS(Long Term Support)版本,Oracle提供5年的维护周期,以及3年的付费额外支持,一共8年维护周期。

到目前为主,工业界的主力版本依然是Java 8和11. 随着云计算,大数据,以及多核的快速发展,Java社区也在适就这些变化,在随后的Java版本中增加了很多对云场景、新硬件的技术,比如对容器技术的技术,对大内存和NUMA技术的支持。

当前团队从2021年开始就考虑使用ZGC新特性,充分发挥大内存的特点,能更好满足大数据对多核和大内存的发展趋势,目前我们正在考虑使用Java 17 LTS版本来支持大数据领域的技术发展。​

Java重要特性介绍

当前团队当前主流的Java版本是Java8和11,为了让大家对Java高版本特性有更好的了解,我从Java12开始直到Java17最为重要的特性做了初步的分析。由于篇幅有有限和对Java了解深度不足,本文只介绍Java虚拟机领域较新的特性,不涉及Java语言和Java框架发展分析。Java技术的爱好者均可从这里找到完整的Java提案。

因此,本文重点分析Java12到Java17在性能方面和云计算方面取得的进展。

性能​

ZGC

ZGC无疑是Java GC算法的一个大变革,相比之前的GC算法,提供了以下亮眼的特性:

  • 亚毫秒级的最大暂停时间 (最大暂停时间为10ms,但实际暂停时间基本在1ms以内)
  • 暂停时间与堆大小无关,与活跃对象和根引用集合大小无关
  • 堆大小从8M到16T都可以支持

ZGC最早出现在JDK 11中作为实验特性,从Java15开始作为生产特性使用. ZGC为什么能提供这么亮眼的特性,它的关键设计要点有如下:

  • 最大限度的Concurrent: 除了几个非常短和工作量较为固定的阶段需要STW(Stop The World)外,其它阶都是GC线程和业务线程并发执行
  • Region-Based: 可以灵活支持不同大小的Heap
  • Compacting: 使用Compact机制,同时也不再分年轻代和老年代
  • NUMA-aware: 感知机器的NUMA拓扑结构,让线程分配内存让尽量在本地Node上分配内存,让后续内存访延迟更小
  • 使用3颜色指针和读屏碍:这是ZGC一种创新,通过通过读屏障,在Compact阶段,用户线程无须停下来,而是帮着GC线程在访问对象的同时顺便把Compact的工作也做了;3颜色则表示对象指针的3种视图来表示对象是不是正在GC状态,无须在Java对象上标准Mark状态。
  • 使用透明大页(Transparent Huge Page, THP):THP是比Linux默认的hugetlb更灵活,ZGC天生支持THP,让堆对象的访问性能更高,THP(2M页)与4K页内存访问相比,性能会有5~8%的性能提升

ZGC一个回收集周期图示如下:

从上图可以看出,只有Initial Mark,Remark和Initial Transfer过程才需要STW,其它阶段都是GC线程与业务线得程并发执行,不会造成业务线程停顿。上述的3个会产生STW阶段的工作量只与根引用集合大小相关,实际上这几个过程都只需要做一次根对象扫描,耗时非常短,通常在1ms以内,也这是为什么ZGC号称最大停顿时间时只有亚毫秒级。

3颜色指针和读屏障

​为了更方便支持在Compact阶段与业务线程并发执行,ZGC提出了3颜色指针方案:方案原理非常简单,那就是将操作系统分配的一块物理内存,同时映射到3块不同的虚拟地址空间,并且同一个对象的虚拟地址在这3个空间里,只是地址的高位是不同的,低42位是相同。这样就可以通过修改对象引用地址的高位,实现不同的地址视图切换。

上图是3个地址空间的划分,M0,M1和Remapped,中间有一个预留区域,是为了与下图的指针位相匹配的。

Marked 0, Marked 1和Remapped这3个标志位是互斥的,任何时候只能置一个位。当Marked 0置位时,指针指向M0空间,当前Marked 1置位时,指针向批M1空间,当Remapped置位时,指针指向Remapped空间。​

ZGC的几个关键流程如下:

  • 初始化:GC线程扫描所有根引用对象,并将这些引用视图修改为Remapped
  • 并发标记阶段:无论是并行的GC线程扫描到对象时,将对象视图切换成M0,业务线程在些阶段访问到对象时,读屏障代码会将对象视图修改为M0. 这个阶段结束后,对象的地址视图是M0时,表示是一个活跃对象。对象依然停留在Remapped视图时,表示它是非活跃对象,可以回收。
  • 并发转移阶段(compact):这个阶段的功能,是将活跃对象搬依次搬到Region的低地址区。过程是GC线程和业务并发执行,GC线程专注于搬移对象,但此过程里恰巧业务访问对象时,会在读屏障代码里将对象实施转移。对象是否已经转换成功了,全靠地址视图来判定。如果为M0时,说明还没有转移,则转移并将地址视图切换成Remapeed,否则不用做处理(因为有另一个线程帮忙做了)。

以下是JDK社区官方测试结果:

左图是Java标准的的性能测试套结果,数据做了归一处理,将ZGC的max-jOPS归一化成100%。max-jOPS表示吞吐性能,数值越大越好,critical-jOPS反应延迟性能,数据越大越好。从测试效果来看,ZGC比G1有明显的提升,max-jOPS提升10%,critical-jOPS提升约40%。

右图同时是SPECjbb@2015测试时,跟踪到的GC暂停时间。从测试结果来看,ZGC的平均值,99.99分位值和 Max值都在1ms左右,吊打此前任何版本的GC算法。

Virtual Thread

提到Java已发布的Virtual Thread,必须要提起它的真身——协程,而谈到协程,又必须多嘴提一下没有协程前的血泪史。

在刚开始的互联网时代,Appach HTTP server作为web server的实现者,以process per client的方式支持多client并发访问,在并发量达到10K时(可详见C10K问题),操作系统完全无法承受大量进程所占用的资源开销。接着Unix/Linux出现了kqueue/select多路侦听技术,client的消息调度与处理不再交给process或thread调度了,而是每个应用自己来调度client请求。通过类似于Java NIO或者事件回调这种异步处理技术,可以实现一个server高并发,性能表现非常优异,典型的例子就是Nginx。随着异步回调技术的发展,开发人员很快就陷入了回调地狱的陷阱。一个连贯的线性处理逻辑,必须拆分成多个小片段,散落在多个回调事件处理钩子中,可维护性差。在Java领域,使用reactive programming是一个方向,典型的是Vert.X框架帮助开发人员编写反应式程序。

随后协程的出现,能完美解决这个问题。在代码编写上(形式上):client处理代码只需写一个类似于while(1)的函数,读取request,然后process,再发送reponse给client,依次循环直接client主动关闭链接。在实际执行上,server由于大量的client链接,也有有大量的协程存在,但当一个client在recv 等待request时,协程调度器会快速切换另一协程(client)进行处理。 相对于传统的由kernel对线程进行调度,转换成由协程调度器在线程上下文内对协程进行调度,效率非常。

Java的Loom项目就是推动Java协程落地的项目,下图是从编程的复杂性和运行时的可伸缩性,分别几个技术方向的特点,协程是兼顾了编程简单化和可伸缩性两个方面,鱼和熊掌可兼得。

Java19开始提供Virtual Thread技术,作为一个preview特性,这意味Virtual Thread技术并没有定稿,它的实现和API在将来的生产版本也许会改变。

在介绍Virtual Thread编程API前,先说一下相关的技术概念 :

​​

如上图所示,相比传统的Java线程模型来说,增加了Virtual Thread和Platform Thread。 Virtual Thread比较容理解,就是程序代码中的一个Virtual Thread,但是Platform thread是什么呢?这个并不难,它是协程调度器,同时也是协程的执行载体。Virtual thread和Platform Thread关系并不难理解,Plaform Thread自己会执行 Virtual Thread代码,并且它将多个VT代码交织到一起串着执行。这就像操作系统上的线程与硬件CPU的关系,CPU执行线程,并且是将线程代码交织到一起串着执行。

Plaform thread在任一时刻,只能执行一个Virtutal Thread,其它还没有得到执行的Virtual Thread 称为Unmounted Ready Virtual Thread(未绑定的就绪虚执线程),另外还有一些在等待网络报文请求的Virtual Thread称为Unmounted Blocked Virtual Thread(未绑定的阻塞虚拟线程)。

当Virtual Thread执行到会阻塞的函数调用时(比如InputStream中的read方法),Platform会注监控到,然后把该Virtual Thread注册到一个类似于select/epoll的事件侦听器上(了解NIO的朋友应该不陌生),接下来是Platform Thread 会执行相关的功能,并把当前Vritual Thread放到 Blocked列表上,然后从Ready的Virtual Threads上挑一个来执行。当网络报文达到时, 操作通知到Plaform Thread,然后Platform Thread把相应的Virtual Thread唤醒,放到Ready Virtual Thread,等待下一次被选中执行。

下面简要介绍Virtual Thread的API:

创建Virtual Thread 可用 Thread.ofVirtual()方法:

Thread thread = Thread.ofVirtual().name("duke").unstarted(runnable);​

或者使用Thread.Builder来生成一个ThreadFactory,然后可以批量产生多个相同属性的Virtual Thread。

请注意,Virtual Thread在对象上没有创建新的子类,而且用java.lang.thread来表示传统线程或者Virtual Thread,可通过Thread.isVirtual()方法来测试是否为Virtual Thread。

由于Virtual Thread在调用阻塞方法时,都需要Platform Thread进行监控和捕捉,所有涉及IO操作的API都需要进行修改,以支持Virtual Thread功能。因此涉及到了大量的API修改,列表如下:

  • java.util.concurrent
  • Networking
  • http://java.io
  • Java Native Interface (JNI)
  • Debugging (JVM TI, JDWP, and JDI)
  • JDK Flight Recorder (JFR)
  • Java Management Extensions (JMX)
  • java.lang.ThreadGroup

然而Virtual Thread(和协程一样)并不是万能的,它的应用场景是高并发大量网络请求,通过Platform thread快速切换Virtual Thread获得高效的性能,同时提供线性逻辑的编程方式。但对对计算型任务没有任何效果,计算型任务通常是根据CPU个数来划分并行度,中间不涉及大量的线程调度。

Virtual Thread特性到目前为止,还没有进入商用阶段。但在回过头来看看Java发展最为红火时代出现的编程框架,无一例外都使用线程池来解决多大并发问题。这些多线程架框架在持续优化,为开发人员屏蔽了很多技术细节和复杂性。所以Virtual Thread的出现,能否给Java社区带来翻天覆地的变化,我们拭目以待。

Vector API

向量(Vector)这个词相信对大家来说并不陌生,传统的CPU属于SISD(单指令单数据),随着图形计算的需求,SIMD(单指令多数据)功能应运而生。Intel处理也经历了从MMX到SSE,再到现在的AVX512指令。从软件层面说来,很多底层运行库都开始使用SIMD指令来优化内存处理,比如典型的例子是glibc的memcpy函数就使用了SSE指令加速内存拷贝速度,另一个例子应该gcc编译器在-O3优化层级下,对C/C++ for循环也尽力做向量化处理(即用SSE指令进行优化)。

用下面两例代码来体会一下Java编程世界里SISD和SIMD的差异:

// SISD 例子
void scalarComputation(float[] a, float[] b, float[] c) {
   for (int i = 0; i < a.length; i++) {
        c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
   }
}

// SIMD 例子
void vectorComputation(float[] a, float[] b, float[] c) {
    int i = 0;    int upperBound = SPECIES.loopBound(a.length);
    for (; i < upperBound; i += SPECIES.length()) {
        // Boxing: from array to SIMD Vector
        var va = FloatVector.fromArray(SPECIES, a, i);
        var vb = FloatVector.fromArray(SPECIES, b, i);
        // parallel operation for multiple data
        var vc = va.mul(va)
                   .add(vb.mul(vb))
                   .neg();
       // Unboxing: from SIMD Vector to array
        vc.intoArray(c, i);
   }
}

SISD代码,只能依次对每份数据进行运算,而SIMD代码可以每次同时对16份数据(512 / 32 = 16)同时进行运算,是因为AVX512能同时处理位宽为512bit的多份数据。下图可以形象说明SISD和SIMD处理逻辑上的差异。

看似完美的SIMD肯定能带来性线的加速比,但事实并非如此,我们先看一下测试结果

从测试数据来看,效果并不理想。上图是一个基础的向量相加运算,并没有带来任何性能提升;下图是向量FMA (Fused-Multiply-Add)运算,在内存大小不超过L2时获取10+倍的性能加速,但数据量再大起来后,加速比回到2-3倍左右。

在这里我们可以有一些猜测,由于SIMD硬件宽度有限(以AVX512为例),每512bit数据要进行一次Boxing和Unboxing操作(FloatVector.fromArray和FloatVector.toArray),SIMD获得的红利大量分都这些操作的磨损了。

重新审视最新的 JEP (https://openjdk.org/jeps/438)会发现这个问题已在Java意料之中,Vector API有两个实现,其一是上面测试数据所体现的,使用API库的方式实现。第二个则是使用JVM的intrinsics上,通过C2 JIT编译器直接翻译成Native汇编代码,比如上图的代码在Intel X64机器翻译成如下的汇编代码:

0.43%   / │  0x0000000113d43890: vmovdqu 0x10(%r8,%rbx,4),%ymm0
 7.38%  │ │  0x0000000113d43897: vmovdqu 0x10(%r10,%rbx,4),%ymm1
 8.70%  │ │  0x0000000113d4389e: vmulps %ymm0,%ymm0,%ymm0
 5.60%  │ │  0x0000000113d438a2: vmulps %ymm1,%ymm1,%ymm1
13.16%  │ │  0x0000000113d438a6: vaddps %ymm0,%ymm1,%ymm0
21.86%  │ │  0x0000000113d438aa: vxorps -0x7ad76b2(%rip),%ymm0,%ymm0
 7.66%  │ │  0x0000000113d438b2: vmovdqu %ymm0,0x10(%r9,%rbx,4)
26.20%  │ │  0x0000000113d438b9: add    $0x8,%ebx
 6.44%  │ │  0x0000000113d438bc: cmp    %r11d,%ebx
        \ │  0x0000000113d438bf: jl     0x0000000113d43890​

直接把Java语言翻译成Native 代码,少了JDK API中间赚差价。从上面的汇编代码来,完全没有Boxing和Unboxing代码,性能发挥得极致。

从目前看来,向量计算在Java有以下的支持

  • 把向量当作值类型进行处理
  • 支持Vector API通用化,由intrisincs根据硬件特点,翻译相应该的SIMD硬件指令,同时能屏蔽SIMD功能上的差异,无法用硬件实现的退出成非向量指令
  • 应用Intel的SVML数学库,实现SIMD不能提供的计算能力全部转交给SVML来实现。

目前最新的JEP显示,Vector特性依然还是一个实验特性,期待能早日应用到生产中。

Numa感知​

NUMA (Non-uniform memory access) 技术是计算机体系统架构应对多核挑战下的重要优化:为了减少公共内存带宽的压力和瓶颈,它采用了内存分层的访问结构,正如IDC物理网络的Leaf-Spin架构一样。可从下图的物理结构一窥究竟:

上图是典型的Intel处理器当前的配置,分成两个socket,每个Socket有本地的内存,CPU和硬件。这里我们重要关注内存,所有socket内存条是统一编址的,但是CPU访问本地内存时,速度比访问远端socket的内存要快好几倍,因为他们之间需要一个socket间互联通道QPI进行通信。

实际上,硬件和操作系统对NUMA的支持已经是比较友好了,一个进程在申请内存时,操作系统会尽量从本地内存空间中分配内存给程序使用,减少远端内存访问的概率。

但是Java自身托管了Heap内存管理,所以它需要感知 NUMA,才能在适合的位置分配内存,让业务线程访问性能更高。同样由于NUMA对性能的意义重大,Java在ParallelGC是已支持NUMA感知的,但是G1回收算法在Java14之前并没有支持,所以在Java14提出了支持NUMA感知特性。​

G1感知NUMA的原理并不复杂:在Heap初始化时,比较平均地从两个Socket节点申请内存;当业务线程new对象时,优先考虑从线程所在socket的heap空间上分配对象,这样对年轻代对象非常友好。因为年轻代对象生存周期往往很短,所以业务线程访问时往往没有调度到另一个Socket的CPU上,极大概率是本地访问,性能高。但如果本地Heap内存空间不足时,触发GC回收,再分配。但如果万一本地Heap没有空闲内存了,但另一个socket Heap有较多的空闲空间,G1会failback到远端Heap分配对象。

下图是SPECjbb@2015的测试结果,G1支持NUMA感知是在JDK-14 b24合入的,B23是它前一个版本,它们的性能对比最能说明优化效果。该测试使用了512GB的G1 heap做SPECjbb@2015 benchmark测试,从max-JOPS指标来看,提升了20.64%,而critical-JOPS也有9.52%的提升。

同样地,ZGC在设计是就支持NUMA感知,这样不做详细分析和讨论。​

云原生​

GraalVM

在云计算之前, Java有着独特的优势,在企业应用领域大红大紫,开源大数据领域all in Java。但在随着云计算的蓬勃发展,Java的优势反而成了它的不足。 传统的Java程序难以支持毫秒级的启动时间,厚重的应用编程框架,大大逊色于轻量级Javacript来开发FaaS应用,Java实际应过程遇到的各种性能和多语言问题。 为了应对云计算,以及当下发展火热云原生技术, Oracle研发了GraalVM,一种通用JVM解决方案,以解决在云计算场景的痛点。

GraalVM有以下的技术特点:

  • 支持大量的编程语言:包括原本就支持的JVM语言,以及基于LLVM的语言(C/C++),动态语言:Javascript, Ruby, Python, R语言,也支持动态语言引擎:各种Javascript引擎,FastR引擎,RubyTruffle引擎
  • 支持静态编译:把各种语言直接翻译成Native代码,应用程序进行二进制执行,大大加速了应该程的启动时间
  • 高性能:完全Native代码运行,没有翻译解释执行,同时也消除跨语言调用的成本
  • 底噪小,快速启动,多语言联合调用:解决了传统Java应用需要占用大量内存, 以及启动慢问题,对Java应用上云提供便利

下图是GraalVM官网给出的生态系统:

启动时间和内存占用,这两个云计算极为关注的竞争力,它的表现非常优异,如下两图的测试结果可看到。

上两个用Java生态下的3个应用进行测试对比,启动时间快50多倍,内存底躁降低了5倍以上。

在跨语言方面,GraalVM提供了一种在多语言之间无逢传值的方法,而不像传统方式那样需要序列化和反序列化,大大提高的跨语言的性能。正是因为跨语言能力的出现,开发者可以很容易利用另一个语言最新的库进行软件开发,这种便利性大大提升了研发效率。

启动时间是Java整个社区面临的最大挑战之一。下图清楚地展示了为什么Java启动这么慢,JVM有太多的初始化工作需要做。

这个问题最彻底的做法是将Java代码直接翻译成Native,程序可以马上直接运行,不需要加载字节码过程和第一次的解释执行,性能效果最优。由于没有了JVM,JIT组件,可以省去这部分组件所占用的内存开销,也能减少应用image的体积,这完全符合原生的理念和方法。

Java在GraalVM之前并非没有尝试走过这条路,从Java 2之前的GCJ,再到后来的Excelsior JET,再到现在GraalVM提出的通过静态编译解决Java启动慢问题,一路走过,遇到不少挑战。

但这并不是一条容易的技术路线,Java从Bytecode层面来说,它是一个动态语言,它支持灵活的反射技术,支持AOP(Aspect-Oriented Programming)编译技术,意味着JVM给开发者开放了灵活的动态编程技术。因此,在这种便利下,很多信息(比如方法代码,甚至是类)都是无法在编译时确定的,而是在运行时动态地由代码来生成决定,所以无法在在编译阶段直接生成Native。这也就是为什么在此之前只能做到AOT( ahead-of-time)技术。

但如果要求开发者把上述所有态动性功能都去掉,不需要使用,Java生态下的重要组件,如Srping, Hibernate必然跑不起来,整个Java生态会轰然崩塌。于是一条迂回曲折之路必须会摆在各个架构师前面。前面提到GraalVM支持翻译成Native代码是指其它语言跑在GraalVM可以翻译成Native,或者那些没有使用高级动态功能的 Java可以翻译成Native,但对于绝大多数的Java应用来说,还是一条很长远的路。

容器化

云计算和云原生技术栈当中,容器是最重要的技术之一。以Docker为代表的容器技术流派是最通用的,它是Linux操作系统上的Namespace和cgroup两大技术为支柱。所以,Java支持容器技术变更犹为重要。

Kubernetes作为最重的容器调度平台,在上面部署应用时,当然不少了指定每个pod的 Request和Limit资源要求(包含CPU和memory)。但由于Linux下的Namespace是一种弱隔离,Java应用程序运行在Kubernetes上之后,它仍然要读取/proc/meminfo和/proc/cpuinfo来获取它运行环境的资源情况,来决定Java Heap应该开多大,池程池应该开多大。如果一个Java容器只申请了4C8G(4 core and 8 G memory)资源,但跑在一个96C和512G的机器上面。那它会理所当然地认识它有96个Core可运行,接近500G内存可自由使用。但在云原环境上,这样做很容易带来致命的问题,那就是不断地触发OOM,服务难以正常提供。当然开发者可以通过Java环境变量传递各种资源约束,但这种做法难以适合一个应用跑在多种不同的资源环境上。

自Java17开始,OpenJDK支持CgroupV2。JDK会读取它所在的cgroup配置来感知它自己的CPU和memory配置,而不是从原来的/proc/meminfo和/proc/cpuinfo来获取这些信息。 下面是个最简例子,展示了容器里面的java从Cgroup里读到了CPU和memory的配置。

$ java -XshowSettings:system -version
Operating System Metrics:
    Provider: cgroupv2
    Effective CPU Count: 2
    CPU Period: 100000us
    CPU Quota: 200000us
    CPU Shares: 1024us
    List of Processors: N/A
    List of Effective Processors, 4 total:
    0 1 2 3
    List of Memory Nodes: N/A
    List of Available Memory Nodes, 1 total:
    0
    Memory Limit: 1.00G
    Memory Soft Limit: 800.00M
    Memory & Swap Limit: 1.00G

openjdk version "17.0.2" 2022-01-18
OpenJDK Runtime Environment 21.9 (build 17.0.2+8)
OpenJDK 64-Bit Server VM 21.9 (build 17.0.2+8, mixed mode, sharing 

当然,Java应没有屏去以往的获得资源方式,而且完全兼容,同时也支持通过API获取Cgroup的配置,具体的使用方式请参考相关的技术文档。有了这一能力,Java更贴近云原生,用户体验更好。

小结

本文重点分析Java12到Java17的一些重进展,以及一些现在还没有完全成熟的技术,比如Vector API, GraalVM。从收益来说,免费的性能收益是令人激动的,对云原生的支持也让运维成本大大降小。但除了这两个方面,Java还涉及很多其它的领域,比如编程语言能力的拓展,开发和调试工具的发展,对整个语言生态极其重要。希望有机会再做这些方面的分析和总结。

参考文献

  1. 从 JDK 9 到 19,认识一个新的 Java 形态(内存篇)https://mp.weixin.qq.com/s/cF6JgJIOCF6Jxg520rRbLA
  2. Java 18: Vector API — Do we get free speed-up? https://medium.com/@Styp/java-18-vector-api-do-we-get-free-speed-up-c4510eda50d2
  3. JDK ZGC introduction https://wiki.openjdk.org/display/zgc/Main
  4. 新一代垃圾回收器ZGC的探索与实践 https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html
  5. JEP 426: Vector API (Fourth Incubator) https://openjdk.org/jeps/426
  6. JEP 425: Virtual Threads (Preview) https://openjdk.org/jeps/425
  7. 云原生时代,Java 的危与机 https://www.infoq.cn/article/rqfww2r2zpyqiolc1wbe
  8. Java 17: What’s new in OpenJDK's container awareness https://developers.redhat.com/articles/2022/04/19/java-17-whats-new-openjdks-container-awareness
  9. NUMA-Aware Memory Allocation for G1 GC https://sangheon.github.io/2020/11/03/g1-numa.html

编辑于 2023-12-29 16:22・IP 属地广东

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1352880.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机创新协会冬令营——暴力枚举题目01

首先是欢迎大家参加此次的冬令营&#xff0c;我们协会欢迎所有志同道合的同学们。话不多说&#xff0c;先来看看今天的题目吧。 题目 力扣题号&#xff1a;2351. 第一个出现两次的字母 注&#xff1a;下述题目和示例均来自力扣 题目 给你一个由小写英文字母组成的字符串 s &…

(ros2)控制gazebo移动的话题:

gazebo并不是ros2内自带的&#xff0c;是一个独立的软件&#xff0c;需要安装ros_gz功能被把ros2消息转换为gazebo可以识别的命令&#xff0c;并且把gazebo状态转换为ros2信息&#xff0c;所以可以认为ros_gz_bridge节点就是gazebo&#xff0c; 这个节点只接收2个话题&#xf…

Yarn的安装与使用详细介绍

什么是yarn Apache Hadoop YARN &#xff08;Yet Another Resource Negotiator&#xff0c;另一种资源协调者&#xff09;是一种新的 Hadoop 资源管理器&#xff0c;它是一个通用资源管理系统&#xff0c;可为上层应用提供统一的资源管理和调度&#xff0c;它的引入为集群在利用…

2024年天津仁爱学院专升本专业课考试考场安排及准考证打印的通知

天津仁爱学院2024年高职升本科专业课考试通知 一、考试科目及时间 天津仁爱学院2024年高职升本科专业课考试定于2024年1月9日9&#xff1a;00-14&#xff1a;00举行。考试地点&#xff1a;天津市静海区团泊新城博学苑&#xff0c;天津仁爱学院。具体考试科目时间安排如下表&a…

提高工作效率的Postman环境变量使用方法

在 Postman 中&#xff0c;用 Environments 来管理环境变量。我们在开发的过程中&#xff0c;往往会用到多个环境&#xff1a;开发环境&#xff0c;测试环境&#xff0c;UAT 环境&#xff0c;生产环境等。我们要调用不同环境的 API 时&#xff0c;只需切换 Postman 的 Environm…

ISP 基础知识积累

Amber&#xff1a;现有工作必要的技术补充&#xff0c;认识需要不断深入&#xff0c;这个文档后续还会增加内容进行完善。 镜头成像资料 ——干货满满&#xff0c;看懂了这四篇文章&#xff0c;下面的问题基本都能解答 看完思考 1、ISP 是什么&#xff0c;有什么作用&#xff…

php安装扩展event 提示 No package ‘openssl‘ found 解决方法

在使用pecl编译安装最新版event模块的时候提示 No package openssl found , 可是本机是安装了openssl的, 编译时找不到, 大概率就是环境配置的问题了, 增加 OPENSSL_CFLAGS OPENSSL_LIBS环境变量即可解决. 异常提示信息: checking for openssl > 1.0.2... no configure: …

【数据库原理】(5)关系数据库的关系数据结构

关系及相关概念 在关系模型中,无论是实体还是实体之间的联系均由关系(二维表)来表示。 1.域&#xff08;Domain&#xff09; 定义&#xff1a;域是一组具有相同数据类型的值的集合。例子&#xff1a;实数集合、整数集合、英文字母集合等。 2.笛卡儿积&#xff08;Cartesian…

学习Vue单文件组件总结

今天主要学习了组件实例对象的一个重要内置关系和单文件组件。先说一下实例对象的内置关系&#xff0c;在这里要对JS中的原型链有一定的基础&#xff0c;Vue构造函数的prototype原型指向的是Vue的原型对象&#xff0c;new出来的Vue实例对__proto__同样指向的是Vue的原型对象&am…

Hex 文件类型字段详解

文章目录 地址类型字段详解02&#xff08;扩展段地址记录&#xff09;&#xff1a;指定后续数据记录的向左移动4位后开始计算地址04&#xff08;扩展线性地址记录&#xff09;&#xff1a;指定后续数据记录的起始地址的高16位 地址类型字段详解 02&#xff08;扩展段地址记录&…

18、BLIP

简介 github BLIP提出了一种基于预训练的方法&#xff0c;通过联合训练视觉和语言模型来提升多模态任务的性能。 BLIP(Bootstrapping Language-Image Pretraining)是salesforce在2022年提出的多模态框架&#xff0c;是理解和生成的统一&#xff0c;引入了跨模态的编码器和解码…

RocketMQ5.0延时消息时间轮算法

前言 RocketMQ 相较于其它消息队列产品的一个特性是支持延时消息&#xff0c;也就是说消息发送到 Broker 不会立马投递给消费者&#xff0c;要等待一个指定的延迟时间再投递&#xff0c;适用场景例如&#xff1a;下单后多长时间没付款系统自动关闭订单。 RocketMQ 4.x 版本的延…

【LeetCode-剑指offer】-- 9.乘积小于K的子数组

9.乘积小于K的子数组 方法&#xff1a;滑动窗口 关于为什么子数组数目为j-11。这时候就要理解采用滑动窗口的思路其实是枚举子数组的右端点&#xff0c;然后来找到满足条件的最小左端点。也即当得到满足条件的窗口时&#xff0c;就意味着得到了以 j 作为右端点时满足条件的左端…

框架的灵魂之笔-反射

反射&#xff1a;框架的灵魂 类加载器 概述&#xff1a;当程序要使用某个类的时候&#xff0c;如果该类还未被加载到内存中&#xff0c;则系统会通过以下三个步骤 ①类的加载 ②类的连接 ③类的初始化来对类进行初始化。如果不出现意外情况&#xff0c;JVM将会连续完成这三个步…

momentjs计算两个时间差返回时分秒

// 导入 Moment.js 模块 const moment require(moment);// 定义起始时间和结束时间 const startTime 2021-09-30T14:30:00; // 格式必须符合 ISO8601&#xff08;YYYY-MM-DDTHH:mm:ss&#xff09; const endTime 2021-09-30T15:45:00;// 创建 Moment.js 对象来表示起始时间和…

CentOS 7 实战指南:文本处理命令详解

前言 在Linux系统中&#xff0c;文本处理是非常基础却又必不可少的一项技能。如果你正在使用CentOS系统&#xff0c;那么学会如何利用文本操作命令来高效地处理文本文件无疑将会是一个强有力的工具。 本篇文章将介绍一些最常用和最实用的文本操作命令&#xff0c;并通过详尽的…

如何自动生成 API 接口文档 - 一份详细指南

本篇文章详细教你如何使用 Apifox 的 IDEA 插件实现自动生成接口代码。好处简单总结有以下几点&#xff1a; 自动生成接口文档&#xff1a; 不用手写&#xff0c;一键点击就可以自动生成文档&#xff0c;当有更新时&#xff0c;点击一下就可以自动同步接口文档&#xff1b;代码…

金和OA UserWebControl.UserSelect.ashx 信息泄露漏洞

产品简介 金和网络是专业信息化服务商&#xff0c;为城市监管部门提供了互联网监管解决方案&#xff0c;为企事业单位提供组织协同OA系统升开发平台&#xff0c;电子政务一体化平台智慧电商平合等服务 漏洞概述 金和OA UserWebControl.UserSelect.AjaxServiceMethod,UserWeb…

秋招复习之迭代与递归

目录 前言 递归 1. 调用栈 2. 尾递归 3. 递归树 总结 前言 复习中ing&#xff0c;递归我总是迷迷糊糊的&#xff0c;这里有点醍醐灌顶。迭代是自下而上&#xff0c;从最基础的步骤开始&#xff0c;然后不断重复或累加这些步骤&#xff0c;直到任务完成。递归是自上而下&…

共识算法介绍

文章目录 共识算法Paxos 算法三种角色一致性提交算法prepare 阶段accept 阶段commit 阶段 CAP 定理BASE 理论Zookeeper 算法实现三类角色三个数据三种模式四种状态消息广播算法Leader选举算法 共识算法 Paxos 算法 Paxos 算法是莱斯利兰伯特(Leslie Lamport)1990 年提出的一种…