JVM——执行引擎

文章目录

  • 1、概述
  • 2、计算机语言的发展史
    • 2.1、机器码
    • 2.2、汇编语言
    • 2.3、高级语言
    • 2.4、字节码
  • 3、Java代码编译和执行过程
  • 4、解释器
  • 5、JIT编译器
    • 5.1、为什么HotSpot VM同时存在JIT编译器和解释器
    • 5.2、热点代码探测确定何时JIT
    • 5.3、设置执行模式
    • 5.4、C1编译器和C2编译器
  • 6、AOT编译器和Graal编译器
  • 7、小结

我们知道Java源文件经过编译之后生成class文件,class文件加载到内存中,此时物理机器并不能直接执行代码,因为它没办法识别class文件中的内容,此时就需要执行引擎(Execution Engine)来做相应的处理。本贴将主要讲解执行引擎在JVM中的作用。

1、概述

执行引擎是JVM核心的组成部分之一。可以把JVM架构分成三部分,如下图所示:
在这里插入图片描述
执行引擎位于JVM的最下层(图中虚线框部分),可以粗略地看到执行引擎负责和运行时数据区交互。

2、计算机语言的发展史

在讲解执行引擎之前,需要知道什么是机器码、汇编语言、高级语言以及为什么会有Java字节码的出现。

2.1、机器码

各种用0和1组成的二进制编码方式表示的指令,叫作机器指令码,简称机器码。计算机发展的初始阶段,人们就用机器码编写程序,我们也称为机器语言。机器语言虽然能够被计算机理解和接受,但和人类的语言差别太大,不易被人们理解和记忆,并且用它编程容易出差错。使用机器码编写的程序一经输入计算机,CPU可以直接读取运行,因此和其他语言编的程序相比,执行速度最快。机器码与CPU紧密相关,所以不同种类的CPU所对应的机器码也就不同。

2.2、汇编语言

由于机器码是由0和1组成的二进制序列,可读性实在太差,于是人们发明了指令。指令就是把机器码中特定的0和1序列,简化成对应的指令(一般为英文简写,如mov、inc等),可读性稍好,这就是我们常说的汇编语言。在汇编语言中,用助记符(Mnemonics)代替机器码的操作码,用地址符号(Symbol)或标号(Label)代替指令或操作数的地址。

不同的硬件平台,各自支持的指令是有差别的。因此每个平台所支持的指令,称为对应平台的指令集,如常见的x86指令集对应的是x86架构的平台,ARM指令集对应的是ARM架构的平台。不同平台之间指令不可以直接移植。

由于计算机只认识机器码,所以用汇编语言编写的程序还必须翻译成机器码,计算机才能识别和执行。

2.3、高级语言

为了使计算机用户编程序更容易些,后来就出现了各种高级计算机语言。比如C、C++等更容易让人识别的语言。

当计算机执行高级语言编写的程序时,仍然需要把程序解释或编译成机器的指令码。完成这个过程的程序就叫作解释程序或编译程序,如下图所示:
在这里插入图片描述

2.4、字节码

字节码是一种中间状态(中间码)的二进制代码(文件),需要转译后才能成为机器码。字节码主要为了实现特定软件运行和软件环境,与硬件环境无关。如下图所示:
在这里插入图片描述
Java程序可以通过编译器将源码编译成Java字节码,特定平台上的虚拟机将字节码转译为可以直接执行的指令,也就实现了跨平台性。

3、Java代码编译和执行过程

我们知道虚拟机并不是真实存在的,是由软件编写而成的,它是相对于物理机的概念。但是虚拟机和物理机一样都可以执行一系列的计算机指令,其区别是物理机的执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的,而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约地定制指令集与执行引擎的结构体系,执行那些不被硬件直接支持的指令集格式。

JVM装载字节码到内存中,但字节码仅仅只是一个实现跨平台的通用契约而已,它并不能够直接运行在操作系统之上,因为字节码指令并不等价于机器码,它内部包含的仅仅只是一些能够被JVM所识别的字节码指令、符号表,以及其他辅助信息。

如果想要让一个Java程序运行起来,执行引擎的任务就是将字节码指令解释/编译为对应平台上的机器码才可以。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者,就好比两个国家领导人之间的交流需要翻译官一样。

在Java虚拟机规范中制定了JVM执行引擎的概念模型,这个概念模型成为各大发行商的JVM执行引擎的统一规范。执行引擎的工作流程如下图所示:
在这里插入图片描述

  • (1)执行引擎在执行的过程中需要执行什么样的字节码指令完全依赖PC寄存器。
  • (2)每当执行完一项指令操作后,PC寄存器就会更新下一条需要被执行的指令地址。
  • (3)方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象的类型信息。

所有的JVM的执行引擎输入、输出都是一致的,输入的是字节码二进制流,处理过程是字节码解析执行的过程,输出的是执行结果。

大部分的程序代码转换成物理机的目标代码或虚拟机能执行的指令集之前,都需要经过下图中的各个步骤:
在这里插入图片描述
程序源码到抽象语法树的过程属于代码编译的过程,和虚拟机无关,如上个中矩形实线框所示;指令流到解释执行的过程属于生成虚拟机指令集的过程,如上图矩形虚线框所示;优化器到目标代码的过程属于生成物理机目标代码的过程,如上图中最下层云状图形所示。

具体来说,Java代码编译是由Java源码编译器来完成,流程图如下图所示:
在这里插入图片描述
在Java中,javac编译器主要负责词法分析、语法分析和语义分析,最终生成二进制字节码,此过程发生在虚拟机外部。

Java源代码经过javac编译器编译之后生成字节码,Java字节码的执行是由JVM执行引擎来完成,流程图如下图所示:
在这里插入图片描述
可以看到图中有JIT编译器和字节码解释器两种路径执行字节码,也就是说可以解释执行,也可以编译执行。Java是一种解释类型的语言,其实JDK 1.0时代,将Java语言定位为“解释执行”还是比较准确的,再后来,Java也发展出可以直接生成本地代码的编译器,所以Java语言就不再是纯粹的解释执行语言了。现在JVM在执行Java代码的时候,通常都会将解释执行与编译执行结合起来进行,这也就是为什么现在Java语言被称为半编译半解释型语言的原因。

4、解释器

解释器的作用是当JVM启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的机器码执行。

JVM设计者的初衷是为了满足Java程序实现跨平台特性,因此避免采用静态编译的方式直接生成机器码,从而诞生了实现解释器在运行时采用逐行解释字节码执行程序的想法。如下图所示:
在这里插入图片描述
如果不采用字节码文件的形式,我们就需要针对不同的平台(Windows、Linux、Mac)编译不同的机器指令,那么就需要耗费很多精力和时间;如果采用了字节码的形式,那么就只需要从源文件编译到字节码文件即可,虽然在不同的平台上,但是JVM中的解释器可以识别同一套字节码文件,大大提高了开发效率。解释器真正意义上所承担的角色就是一个运行时的“翻译者”,将字节码文件中的内容“翻译”为对应平台的机器码执行。

在Java的发展历史里,一共有两套解释执行器,分别是古老的字节码解释器和现在普遍使用的模板解释器。字节码解释器在执行时通过纯软件代码模拟字节码的执行,效率非常低下。而模板解释器将每一条字节码和一个模板函数相关联,模板函数中直接产生这条字节码执行时的机器码,从而很大程度上提高了解释器的性能。在HotSpot VM中,解释器主要由Interpreter模块和Code模块构成。Interpreter模块实现了解释器的核心功能,Code模块用于管理HotSpot VM在运行时生成的机器码。

由于解释器在设计和实现上非常简单,因此除了Java语言之外,还有许多高级语言同样也是基于解释器执行的,比如Python、Perl、Ruby等。但是在今天,基于解释器执行已经沦落为低效的代名词。为了解决低效这个问题,JVM平台支持一种叫作即时编译的技术。即时编译的目的是避免函数被解释执行,而是将整个函数体编译成机器码,每次函数执行时,只执行编译后的机器码即可,这种方式可以使执行效率大幅度提升。不过无论如何,基于解释器的执行模式仍然为中间语言的发展做出了不可磨灭的贡献。

5、JIT编译器

JIT编译器(Just In Time Compiler)的作用就是虚拟机将字节码直接编译成机器码。但是现代虚拟机为了提高执行效率,会使用即时编译技术将方法编译成机器码后再执行。

在JDK 1.0时代,JVM完全是解释执行的,随着技术的发展,现在主流的虚拟机中大都包含了即时编译器。

HotSpot VM是目前市面上高性能虚拟机的代表作之一。它采用解释器与即时编译器并存的架构。在JVM运行时,解释器和即时编译器能够相互协作,各自取长补短,尽力去选择最合适的方式来权衡编译本地代码的时间和直接解释执行代码的时间。

在此大家需要注意,无论是采用解释器进行解释执行,还是采用即时编译器进行编译执行,都是希望程序执行要快。最终字节码都需要被转换为对应平台的机器码。

可以使用jconsole工具查看程序的运行情况,代码如下所示:
在这里插入图片描述
结果如下图所示:
在这里插入图片描述
可以看见用到了JIT编译器。关于jconsole工具的使用可以查看后面的内容。

5.1、为什么HotSpot VM同时存在JIT编译器和解释器

既然HotSpot VM中已经内置JIT编译器了,那么为什么还需要再使用解释器来“拖累”程序的执行性能呢?比如JRockit VM内部就不包含解释器,字节码全部都依靠即时编译器编译后执行。

首先明确,当程序启动后,解释器可以马上发挥作用,省去编译的时间,立即执行。编译器要想发挥作用,把代码编译成机器码,需要一定的执行时间,但编译为机器码后,执行效率高。

尽管JRockit VM中程序的执行性能会非常高效,但程序在启动时必然需要花费更长的时间来进行编译(即“预热”)。对于服务端应用来说,启动时间并非是关注重点,但对于那些看中启动时间的应用场景而言,或许就需要采用解释器与即时编译器并存的架构来换取一个平衡点。在此模式下,当JVM启动时,解释器可以首先发挥作用,而不必等待JIT全部编译完成后再执行,这样可以省去许多不必要的编译时间。随着程序运行时间的推移,即时编译器逐渐发挥作用,根据热点代码探测功能,将有价值的字节码编译为机器码,并缓存起来,以换取更高的程序执行效率。

注意解释执行与编译执行在线上环境存在微妙的辩证关系。机器在热机状态可以承受的负载要大于冷机状态。如果以热机状态时的流量进行切流,可能使处于冷机状态的服务器因无法承载流量而假死。

在生产环境发布过程中,以分批的方式进行发布,根据机器数量划分成多个批次,每个批次的机器数至多占到整个集群的1/8。曾经有这样的故障案例,某程序员在发布平台进行分批发布,在输入发布总批数时,误填写成分为两批发布。如果是热机状态,在正常情况下一半的机器可以勉强承载流量,但由于刚启动的JVM均是解释执行,还没有进行热点代码统计和JIT动态编译,导致机器启动之后,当前l/2发布成功的服务器马上全部宕机,此故障证明了JIT的存在。

如下图所示:
在这里插入图片描述
以人类语言为例,形象生动地展示了Java语言中前端编译器、解释器和后端编译器(即JIT编译器)共同工作的流程。前端编译器将不同的语言统一编译成字节码文件(即“乌拉库哈吗哟”),这些信息我们是看不懂的,而是供JVM来读取的。之后可以通过解释器逐行将字节码指令解释为本地机器指令执行,或者通过JIT把热点代码编译为本地机器指令执行。

在此我们要说明一点,Java语言的“编译期”其实是一段“不确定”的操作过程,因为它可能是指一个前端编译器(其实叫“编译器的前端”更准确一些)把.java文件转变成.class文件的过程。也可能是指虚拟机的后端运行期编译器(JIT编译器,Just In Time Compiler)把字节码转变成机器码的过程。还可能是指使用静态提前编译器(AOT编译器,Ahead Of Time Compiler)直接把.java文件编译成本地机器代码的过程。

5.2、热点代码探测确定何时JIT

是否需要启动JIT编译器将字节码直接编译为对应平台的机器码需要根据代码被调用执行的频率而定。那些需要被编译为本地代码的字节码也被称为“热点代码”,JIT编译器在运行时会针对那些频繁被调用的“热点代码”做出深度优化,将其直接编译为对应平台的机器码,以此提升Java程序的执行性能。

一个被多次调用的方法,或者是一个方法体内部循环次数较多的循环体都可以被称为“热点代码”,因此都可以通过JIT编译器编译为机器码,并缓存起来。

一个方法被多次调用的时候,从解释执行切换到编译执行是在两次方法调用之间产生的,因为上一次方法在被调用的时候还没有将该方法编译好,所以仍然需要继续解释执行,而不需要去等待程序被编译,否则太浪费时间了,等再次调用该方法的时候,发现该方法已经被编译好,那么就会使用编译好的机器码执行了。

还有一种情况就是一个方法体内包含大量的循环的代码,比如下面的代码:
在这里插入图片描述
main()方法被执行的次数只有一次,但是方法体内部有一个循环20000次的循环体,这种情况下,就需要将循环体编译为机器码,而不是将main()方法编译为机器码,这个时候就需要在循环入口处判断是否该循环体已经被编译为机器码。由于这种编译方式不需要等待方法的执行结束,因此也被称为栈上替换编译,或简称OSR(On Stack Replacement)编译。

一个方法究竟要被调用多少次,或者一个循环体究竟需要执行多少次循环才可以达到这个标准?必然需要一个明确的阈值,JIT编译器才会将这些“热点代码”编译为机器码执行。这里主要依靠热点探测功能,比如上面代码的循环次数为20000次,那么就可能在循环执行5000次的时候开始被编译,然后在第5200次循环的时候开始使用机器码,中间的20次循环依然是解释执行,因为编译也是需要消耗时间的。

目前HotSpot VM所采用的热点探测方式是基于计数器的热点探测。HotSpot VM会为每一个方法都建立两个不同类型的计数器,分别为方法调用计数器(Invocation Counter)和回边计数器(Back Edge Counter)。方法调用计数器用于统计方法的调用次数,回边计数器则用于统计循环体执行的循环次数。

方法调用计数器的默认阈值在Client模式下是1500次,在Server模式下是10000次。超过这个阈值,就会触发JIT编译。这个阈值可以通过虚拟机参数-XX:CompileThreshold来手动设定。

一般而言,如果以缺省参数启动Java程序,方法调用计数器统计的是一段时间之内方法被调用的次数。当超过一定的时间限度,如果方法的调用次数没有达到方法调用计数器的阈值,这个方法的调用计数器的数值调整为当前数值的1/2,比如10分钟之内方法调用计数器数值为1000,下次执行该方法的时候,方法调用计数器的数值从500开始计数。这个过程称为方法调用计数器热度的衰减(Counter Decay),而这段时间就称为此方法统计的半衰周期(Counter Half Life Time),可以使用-XX:CounterHalfLifeTime参数设置半衰周期的时间,单位是秒。可以使用JVM参数“-XX:-UseCounterDecay”关闭热度衰减,让方法计数器统计方法调用的绝对次数,这样,只要系统运行时间足够长,绝大部分方法都会被编译成机器码。一般而言,如果项目规模不大,并且产品上线后很长一段时间不需要进行版本迭代,都可以尝试把热度衰减关闭,这样可以使Java程序在线上运行的时间越久,执行性能会更佳。

如图下图所示:
在这里插入图片描述
当一个方法被调用时,会先检查该方法是否存在被JIT编译过的版本,如果存在,则编译执行。如果不存在已被编译过的版本,则将此方法的调用计数器值加1,然后判断方法计数器的数值是否超过设置的阈值。如果已超过阈值,那么将会向JIT申请代码编译,如果没有超过阈值,则继续解释执行。

回边计数器的作用是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge),回边可简单理解为循环末尾跳转到循环开始。回边计数器的流程如下所示,当程序执行过程中遇到回边指令时,判断是否已经存在编译的机器码,如果存在,则编译执行即可,如果不存在,则回边计数器加1,再次判断是否超过阈值,如果没有超过,则解释执行,如果超过阈值,则向编译器提交编译请求,之后编译器开始编译代码,程序继续解释执行。回边计数器的阈值可以通过参数“-XX:OnStackReplacePercentage”设置。显然,建立回边计数器统计的目的就是为了触发OSR编译,如下图所示:
在这里插入图片描述

5.3、设置执行模式

缺省情况下HotSpot VM采用解释器与即时编译器并存的架构,使用java –version命令可以查看,如下所示,mixed mode表示解释器与即时编译器并存。
在这里插入图片描述
当然,开发人员可以根据具体的应用场景,通过下面的命令显式地为JVM指定在运行时到底是完全采用解释器执行,还是完全采用即时编译器执行。

-Xint命令表示完全采用解释器模式执行程序,如下所示:

     C:\Users\Administrator>java -Xint  -versionjava version"1.8.0_131"Java(TM)SE Runtime Environment(build 1.8.0_131-b11)Java HotSpot(TM)64-Bit Server VM(build 25.131-b11,interpreted mode)

-Xcomp命令表示完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行,如下所示:

     C:\Users\Administrator>java -Xcomp  -versionjava version"1.8.0_131"Java(TM)SE Runtime Environment(build 1.8.0_131-b11)Java HotSpot(TM)64-Bit Server VM(build 25.131-b11,compiled mode)

-Xmixed命令表示采用解释器和即时编译器的混合模式共同执行程序,如下所示:

     C:\Users\Administrator>java -Xmixed  -versionjava version"1.8.0_131"Java(TM)SE Runtime Environment(build 1.8.0_131-b11)Java HotSpot(TM)64-Bit Server VM(build 25.131-b11,mixed mode)

5.4、C1编译器和C2编译器

在HotSpot VM中内嵌有两个JIT编译器,分别为Client Compiler和Server Compiler,通常简称为C1编译器和C2编译器。开发人员可以通过如下命令显式指定JVM在运行时到底使用哪一种即时编译器。

  • (1)-client:指定JVM运行在Client模式下,并使用C1编译器。C1编译器会对字节码进行简单和可靠的优化,耗时短,以达到更快的编译速度。
  • (2)-server:指定JVM运行在Server模式下,并使用C2编译器。C2进行耗时较长的优化,以及激进优化,但优化的代码执行效率更高。

在不同的编译器上有不同的优化策略,C1编译器上主要有方法内联,去虚拟化、冗余消除。

  • (1)方法内联:将引用的函数代码编译到引用点处,这样可以减少栈帧的生成,减少参数传递以及跳转过程。
  • (2)去虚拟化:对唯一的实现类进行内联。
  • (3)冗余消除:在运行期间把一些不会执行的代码折叠掉。

C2的优化主要是在全局层面,逃逸分析是优化的基础。基于逃逸分析在C2上有如下几种优化。

  • (1)标量替换:用标量值代替聚合对象的属性值。
  • (2)栈上分配:对于未逃逸的对象分配对象在栈而不是堆。
  • (3)同步消除:清除同步操作,通常指synchronized。

Java分层编译(Tiered Compilation)策略:不开启性能监控的情况下,程序解释执行可以触发C1编译,将字节码编译成机器码,可以进行简单优化。如果开启性能监控,C2编译会根据性能监控信息进行激进优化。不过在Java 7版本之后,一旦开发人员在程序中显式指定命令“-server”时,默认将会开启分层编译策略,由C1编译器和C2编译器相互协作共同来执行编译任务。一般来讲,JIT编译出来的机器码性能比解释器高。C2编译器启动时长比C1编译器慢,系统稳定执行以后,C2编译器执行速度远远快于C1编译器。

默认情况下HotSpot VM则会根据操作系统版本与物理机器的硬件性能自动选择运行在哪一种模式下,以及采用哪一种即时编译器。

对于32位Windows操作系统,不论硬件什么配置都会默认使用Client模式,可以执行“java -server -version”命令,切换为Server模式,但已经是Server模式的,不能切换为Client模式。对于32位其他类型的操作系统,如果内存配置为2GB或以上且CPU数量大于或等于2,默认情况会以Server模式运行,低于该配置依然使用Client模式。64位的操作系统只有Server模式。

对于开发人员来讲,基本都是64位的操作系统了,因为32位的内存限制为4GB,显得捉襟见肘。现在生产环境上,基本上都是Server模式。所以我们只需要掌握Server模式即可,Client模式基本不会使用了。

6、AOT编译器和Graal编译器

JDK 9引入了AOT编译器(Ahead Of Time Compiler,静态提前编译器)及AOT编译工具jaotc。将所输入的class文件转换为机器码,并存放至生成的动态共享库之中。

所谓AOT编译,是与即时编译相对立的一个概念。我们知道,即时编译指的是在程序的运行过程中,将字节码转换为可在硬件上直接运行的机器码,并部署至托管环境中的过程。而AOT编译指的则是,在程序运行之前,便将字节码转换为机器码的过程,也就是说在程序运行之前通过jaotc工具将class文件转换为so文件。

AOT编译的最大好处是JVM加载已经预编译成二进制库,可以直接执行,无须通过解释器执行,不必等待即时编译器的预热,减少Java应用给人带来“第一次运行慢”的不良体验。把编译的本地机器码保存到磁盘,不占用内存,并可多次使用。但是破坏了Java“一次编译,到处运行”的特性,必须为不同硬件编译对应的发行包,降低了Java链接过程的动态性,加载的代码在编译工作前就必须全部已知。

自JDK 10起,HotSpot又加入一个全新的即时编译器——Graal编译器。它的编译效果在短短几年内就追平了C2编译器,未来可期。目前,它还依然带着“试验状态”的标签,需要使用参数“-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler”去激活,才可以使用。

7、小结

讲述了执行引擎在JVM中起到的作用,执行引擎充当了将class文件中的内容翻译为机器语言的译者,使得物理机器可以识别,进而使得程序可以执行。HotSpot VM中的执行引擎同时存在解释器和JIT编译器,即代码可以解释执行,也可以编译执行。从执行效率上讲,编译执行要比解释执行的效率高。从JVM启动时间来看,解释器可以首先发挥作用,而不必等待JIT全部编译完成后再执行,这样可以省去许多不必要的编译时间编译执行。此外,是否需要启动JIT编译器将字节码直接编译为对应平台的机器码需要根据代码被调用执行的频率而定,尽管如此,程序编译执行仍是未来的发展方向。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

想到2024年最有前景的副业创业机会?那这个项目你绝对不能错过!

大家好,我是电商花花。 现在看到别人要么都有自己的副业,要么都在做创业项目,你是不是也看的激情澎湃,想要做抖音小店,想要创业赚钱。 2024年做什么副业、创业有盼头?我觉得还得是抖音小店无货源这个电商…

【异常处理】sbt构建Chisel库时出现extracting structure failed:build status:error的解决办法

文章目录 报错背景:解决思路:①IDEA中配置本地的SBT进行下载②更改下载源为华为的镜像站1. 修改sbtconfig.txt2. 增加repositories文件 ③查看报错信息 总结整理的Scala-Chisel-Chiseltest版本信息对应表 报错背景: 最近在写Chisel时&#x…

机器学习--循环神经网络(RNN)1

一、简介 循环神经网络(Recurrent Neural Network)是深度学习领域中一种非常经典的网络结构,在现实生活中有着广泛的应用。以槽填充(slot filling)为例,如下图所示,假设订票系统听到用户说&…

【Pytorch】进阶学习:基于矩阵乘法torch.matmul()实现全连接层

【Pytorch】进阶学习:基于矩阵乘法torch.matmul()实现全连接层 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448…

sentinel docker 基础配置学习

1:去官网下载 Releases alibaba/Sentinel GitHub 2:保存到linux 3:编写dockerfile FROM openjdk:8-jreLABEL authors"xxx" #第二步创建一个文件夹Z RUN mkdir /app #第三步复制jar 到app 下 COPY xxxxxx-1.8.7.jar /app/#第四…

原油数据处理:1.聚类、盐含量测定与近红外光谱快速评估

一、原油种类的聚类分析 在塔里木盆地塔河油田的原油处理过程中,需要对原油进行地球化学特征研究,以了解其成因和特征。根据地球化学手段的综合研究结果,塔河油田奥陶系原油属于海相沉积环境,成熟度较高,正构烷烃分布…

有点NB的免费wordpress主题模板

一个不错的黄色模板,用WP免费主题模板搭建家政服务公司网站。 https://www.wpniu.com/themes/15.html

c++ 常用的STL

前言 写这篇博客目的是为了记录在刷算法题中使用过的STL,因为有些不太常用的会遗忘。这篇博客只是作为笔记,不是详细的STL,因此只会对常用方法说明,不会详细介绍。此外在后面用到新的STL内容时会再补充。 列队 基础列队 基本列…

【linuxC语言】dup、dup2函数

文章目录 前言一、dup函数二、dup2函数三、将标准输出重定向到文件总结 前言 在Linux环境下,dup、dup2以及原子操作都是用于文件描述符管理和处理的重要工具。这些功能提供了对文件描述符进行复制和原子操作的能力,使得在多线程或多进程环境中更加安全和…

FPGA高端项目:FPGA基于GS2971的SDI视频接收+HLS图像缩放+多路视频拼接,提供4套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收转HDMI输出应用本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收OSD多路视频融合叠加应用本方案的SDI接收HLS多路视频融合叠加应用本方案…

华为设备小型园区网方案(有线+无线+防火墙)

(一)配置有线部分 1.配置LSW2 (1)创建相关vlan [LSW2]vlan batch 10 3000 (2)配置连接LSW1的Eth-Trunk1,透传VLAN 10 3000 [LSW2]int Eth-Trunk 1 [LSW2-Eth-Trunk1]port link-type trunk [LSW2…

STM32FreeRTOS任务通知(STM32cube高效开发)

文章目录 一、任务通知(一)任务通知概述1、任务通知可模拟队列和信号量2、任务通知优势和局限性 (二) 任务通知函数1、xTaskNotify()发送通知值不返回先前通知值的函数2、xTaskNotifyFromISR()发送通知函数ISR版本3、x…

GitHub和Gitee的基本使用和在IDEA中的集成

文章目录 【1】GitHub1.创建仓库2.增加和修改文件3.创建分支4.删除仓库5.远程仓库下载到本地 【2】Gitee1.创建仓库2.远程仓库下载到本地. 【3】IDEA集成GitHub【4】IDEA集成Gitee1.在Gitee中修改,同步到本地2.从Gitee中下载项目 【1】GitHub 1.创建仓库 先登陆这…

MySQL实战:SQL优化及问题排查

有更合适的索引不走,怎么办? MySQL在选取索引时,会参考索引的基数,基数是MySQL估算的,反映这个字段有多少种取值,估算的策略为选取几个页算出取值的平均值,再乘以页数,即为基数 查…

数据结构——堆的应用 堆排序详解

💞💞 前言 hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 💥个人主页&#x…

C#,排列组合的堆生成法(Heap’s Algorithm for generating permutations)算法与源代码

1 排列组合的堆生成法 堆生成算法用于生成n个对象的所有组合。其思想是通过选择一对要交换的元素,在不干扰其他n-2元素的情况下,从先前的组合生成每个组合。 下面是生成n个给定数的所有组合的示例。 示例: 输入:1 2 3 输出&a…

docker安装ES、LogStash、Kibana

文章目录 一、安装Elasticsearch1. 安装Elasticsearch2. 安装IK分词器3. elasticsearch-head 监控的插件4. 配置跨域 二、安装LogStash三、安装kibana四、SpringBoot集成LogStash,将日志输出到ES中五、 启动项目,监控项目运行 提示:以下是本篇…

用这几个工具,写一份简单的产品说明书

产品说明书是任何产品必不可少的一部分。在这个高速运转的消费市场,一份清晰、明了的产品说明书可以让你的产品在同类产品中脱颖而出。然而,制作一份专业级别的产品说明书可能看起来是个挑战。幸运的是,有很多强大的工具可以帮助你轻松制作产…

深入理解Rem适配:移动端网页设计的利器

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Flutter学习9 - http 中 get/post 请求示例

1、配置 http pubspec.yaml dependencies:http: ^0.13.4flutter:sdk: flutterhttp 库最新插件版本查看:https://pub.dev/packages/http不一定要用最新版本 http,要使用项目所能支持的版本 .dart import package:http/http.dart as http;2、示例 &a…