域名购买网站有哪些问题,百度收录多的是哪些网站,怎么撤销网站备案,手机h5网站模板下载1#xff0c;即时编译器
1.1#xff0c;基本概念 常见的编译型语言如C#xff0c;通常会把代码直接编译成CPU所能理解的机器码来运行。而Java为了实现“一次编译#xff0c;处处运行”的特性#xff0c;把编译的过程分成两部分#xff0c;首先它会先由javac编译成通用的…1即时编译器
1.1基本概念 常见的编译型语言如C通常会把代码直接编译成CPU所能理解的机器码来运行。而Java为了实现“一次编译处处运行”的特性把编译的过程分成两部分首先它会先由javac编译成通用的中间形式——字节码实现跨平台然后再由解释器逐条将字节码解释为机器码来执行。所以在性能上Java通常不如C这类编译型语言。 为了优化Java的性能 JVM在解释器之外引入了即时Just In Time编译器当程序运行时解释器首先发挥作用代码可以直接执行。随着时间推移即时编译器逐渐发挥作用把越来越多的代码编译优化成本地代码来获取更高的执行效率。解释器这时可以作为编译运行的降级手段在一些不可靠的编译优化出现问题时再切换回解释执行保证程序可以正常运行。即时编译器极大地提高了Java程序的运行速度而且跟静态编译相比即时编译器可以选择性地编译热点代码省去了很多编译时间也节省很多的空间。 举例来说刚学骑自行车时就像JVM第一次解释执行字节码需要更多的时间和精力。但通过不断练习JIT编译你逐渐掌握了技巧变得更高效。 解释性比编译性慢的原因编译器在编译过程中通常会考虑很多因素。比如汇编指令的顺序。假设我们要将两个寄存器的值进行相加执行这个操作一般只需要一个CPU周期但是在相加之前需要将数据从内存读到寄存器中这个操作是需要多个CPU周期的。编译器一般可以做到先启动数据加载操作然后执行其它指令等数据加载完成后再执行相加操作。由于解释器在解释执行的过程中每次只能看到一行代码所以很难生成上述这样的高效指令序列。而编译器可以事先看到所有代码因此一般来说解释性代码比编译性代码要慢。 1.2底层原理 Java的执行过程整体可以分为两个部分第一步由javac将源码编译成字节码在这个过程中会进行词法分析、语法分析、语义分析编译原理中这部分的编译称为前端编译。接下来无需编译直接逐条将字节码解释执行在解释执行的过程中虚拟机同时对程序运行的信息进行收集在这些信息的基础上编译器会逐渐发挥作用它会进行后端编译——把字节码编译成机器码但不是所有的代码都会被编译只有被JVM认定为的热点代码才可能被编译。 怎么样才会被认为是热点代码呢JVM中会设置一个阈值当方法或者代码块的在一定时间内的调用次数超过这个阈值时就会被编译存入codeCache中。当下次执行时再遇到这段代码就会从codeCache中读取机器码直接执行以此来提升程序运行的性能。 1.3编译器种类 【Client Compiler】注重启动速度和局部的优化。HotSpot VM带有一个 Client Compiler C1编译器。这种编译器启动速度快但是性能比较Server Compiler来说会差一些。C1会做三件事 局部简单可靠的优化比如字节码上进行的一些基础优化方法内联、常量传播等放弃许多耗时较长的全局优化。将字节码构造成高级中间表示High-level Intermediate Representation以下称为HIRHIR与平台无关通常采用图结构更适合JVM对程序进行优化。最后将HIR转换成低级中间表示Low-level Intermediate Representation以下称为LIR在LIR的基础上会进行寄存器分配、窥孔优化局部的优化方式编译器在一个基本块或者多个基本块中针对已经生成的代码结合CPU自己指令的特点通过一些认为可能带来性能提升的转换规则或者通过整体的分析进行指令转换来提升代码性能等操作最终生成机器码。 【Server Compiler】更加关注全局的优化性能会更好但由于会进行更多的全局分析所以启动速度会变慢。Server Compiler主要关注一些编译耗时较长的全局优化甚至会还会根据程序运行的信息进行一些不可靠的激进优化。这种编译器的启动时间长适用于长时间运行的后台程序它的性能通常比Client Compiler高30%以上。目前Hotspot虚拟机中使用的Server Compiler有两种 【C2 Compiler】在Hotspot VM中默认的Server Compiler是C2编译器。【Graal Compiler】从JDK 9开始Hotspot VM中集成了一种新的Server CompilerGraal编译器。 【C2 Compiler】C2编译器在进行编译优化时会使用一种控制流与数据流结合的图数据结构称为Ideal Graph。Ideal Graph表示当前程序的数据流向和指令间的依赖关系依靠这种图结构某些优化步骤尤其是涉及浮动代码块的那些优化步骤变得不那么复杂。 Ideal Graph的构建是在解析字节码的时候根据字节码中的指令向一个空的Graph中添加节点Graph中的节点通常对应一个指令块每个指令块包含多条相关联的指令JVM会利用一些优化技术对这些指令进行优化比如Global Value Numbering、常量折叠等解析结束后还会进行一些死代码剔除的操作。生成Ideal Graph后会在这个基础上结合收集的程序运行信息来进行一些全局的优化这个阶段如果JVM判断此时没有全局优化的必要就会跳过这部分优化。 无论是否进行全局优化Ideal Graph都会被转化为一种更接近机器层面的MachNode Graph最后编译的机器码就是从MachNode Graph中得的生成机器码前还会有一些包括寄存器分配、窥孔优化等操作。 【Graal Compiler】相比C2编译器Graal有这样几种关键特性 JVM会在解释执行的时候收集程序运行的各种信息然后编译器会根据这些信息进行一些基于预测的激进优化比如分支预测根据程序不同分支的运行概率选择性地编译一些概率较大的分支。Graal比C2更加青睐这种优化所以Graal的峰值性能通常要比C2更好。使用Java编写对于Java语言尤其是新特性比如Lambda、Stream等更加友好。更深层次的优化比如虚函数的内联、部分逃逸分析等。 Graal编译器可以通过Java虚拟机参数-XX:UnlockExperimentalVMOptions -XX:UseJVMCICompiler启用。当启用时它将替换掉HotSpot中的C2编译器并响应原本由C2负责的编译请求。 1.4分层编译 Java 7开始引入了分层编译的概念它结合了C1和C2的优势追求启动速度和峰值性能的一个平衡。从JDK 8开始JVM默认开启分层编译。分层编译将JVM的执行状态分为了五个层次。五个层级分别是 解释执行。执行不带profiling的C1代码。执行仅带方法调用次数以及循环回边执行次数profiling的C1代码。执行带所有profiling的C1代码。执行C2代码。 profiling就是收集能够反映程序执行状态的数据。其中最基本的统计数据就是方法的调用次数以及循环回边的执行次数。 通常情况下C2代码的执行效率要比C1代码的高出30%以上。C1层执行的代码按执行效率排序从高至低则是1层2层3层。这5个层次中1层和4层都是终止状态当一个方法到达终止状态后只要编译后的代码并没有失效那么JVM就不会再次发出该方法的编译请求的。服务实际运行时JVM会根据服务运行情况从解释执行开始选择不同的编译路径直到到达终止状态。 图中第①条路径代表编译的一般情况热点方法从解释执行到被3层的C1编译最后被4层的C2编译。如果方法比较小比如Java服务中常见的getter/setter方法3层的profiling没有收集到有价值的数据JVM就会断定该方法对于C1代码和C2代码的执行效率相同就会执行图中第②条路径。在这种情况下JVM会在3层编译之后放弃进入C2编译直接选择用1层的C1编译运行。在C1忙碌的情况下执行图中第③条路径在解释执行过程中对程序进行profiling 根据信息直接由第4层的C2编译。前文提到C1中的执行效率是1层2层3层第3层一般要比第2层慢35%以上所以在C2忙碌的情况下执行图中第④条路径。这时方法会被2层的C1编译然后再被3层的C1编译以减少方法在3层的执行时间。如果编译器做了一些比较激进的优化比如分支预测在实际运行时发现预测出错这时就会进行反优化重新进入解释执行图中第⑤条执行路径代表的就是反优化。 总的来说C1的编译速度更快C2的编译质量更高分层编译的不同编译路径也就是JVM根据当前服务的运行情况来寻找当前服务的最佳平衡点的一个过程。 2C2 Compiler 占用高内存分析
2.1原因解释 C2 Compiler 是JVM在server模式下字节码编译器JVM启动的时候所有代码都处于解释执行模式当某些代码被执行到一定阈值次数这些代码(称为热点代码)就会被 C2 Compiler编译成机器码编译成机器码后执行效率会得到大幅提升。流量进来后大部分代码成为热点代码这个过程中C2 Compiler需要频繁占用CPU来运行当大部分热点代码被编译成机器代码后C2 Compiler就不再长期占用CPU了这个过程也可以看作抖动。 2.2解决方案不放弃C2 【方案一】最直接有效的方法是“预热(warm up)”可以使用Jmeter等压测工具模拟线上访问流量让C2 Compiler预先将热点代码编译成机器码, 减少对正式环境流量的影响。 【方案二】设置JVM启动参数-XX:CICompilerCountthreads。默认是2 可以设置4或6。在默认值下抖动时CPU已经满载设置成更多的线程也不一定起作用但对于CPU“高而不满”的情况会有用能减少抖动时间。 【方案三】修改codeCache的默认大小-XX:ReservedCodeCacheSize300M 【方案四】关闭分层编译-XX:-TieredCompilation -server C2 CompilerThread9 长时间占用CPU解决方案 - 沧海一滴 - 博客园 3堆外内存泄漏排查
3.1生产问题 有个系统从JDK8升级到JDK21垃圾回收算法为分代ZGC算法看上去该算法会倾向于在堆内存高使用率时才触发GC当使用率有毛刺达到100%才触发GC时使得JVM堆堆外元空间容器本身内存使用超过容器物理内存4G达到了K8S的pod阈值直接kill调了pod容器。 3.2解决方案 ZGC的总体逻辑是这样的它其实并非降内存消耗只是降停顿所以其实需要额外的内存空间来完成这件事。 JVM 总内存 ≈ 堆内存Xmx 堆外内存 元空间 线程栈数量 * Xss 额外内存 这个额外内存是需要留下来的。按照你的xmx1.5额外内存基本上最好得在0.850%以上。xms!xmx 这个设置是出现毛刺的原因之一一般会设置一样或者差不多。 4g内存暂时无法变更 # 固定堆内存为 2GB
-Xms2g -Xmx2g
# 提前触发 ZGC 回收
-XX:SoftMaxHeapSize1.7g
# 限制堆外内存为 256MB
-XX:MaxDirectMemorySize256m
# 限制元空间为 256MB
-XX:MaxMetaspaceSize256m
# 限制线程栈大小为 512KB
-Xss512k
# 限制 JVM 总内存为容器的 85%
-XX:MaxRAMPercentage85 4g有希望换到8g # 固定堆内存为 4GB
-Xms4g -Xmx4g
# 提前触发 ZGC 回收
-XX:SoftMaxHeapSize3.2g
# 限制堆外内存为 1GB
-XX:MaxDirectMemorySize1g
# 限制元空间为 512MB
-XX:MaxMetaspaceSize512m
# 限制线程栈大小为 512KB
-Xss512k
# 限制 JVM 总内存为容器的 90%
-XX:MaxRAMPercentage90 思路也很简单额外留的内存最好大于Xmx的50%。然后把用SoftMaxHeapSize做一下平滑GC相当于提前触发下GC避免到峰值时突然来了大对象而又没有GC出空间导致一下冲破最大限制。 3.3排查思路
一次完整的JVM堆外内存泄漏故障排查记录 - 蛮三刀酱 - 博客园
调试排错 - Java 内存分析之堆外内存 | Java 全栈知识体系
记一次堆外内存泄漏排查过程 - AI乔治 - 博客园
Java 堆外内存排查 - 莫那·鲁道的技术博客