当前位置: 首页 > news >正文

JVM性能调优的基础知识 | JVM内部优化与运行时优化

目录

JVM内部的优化逻辑

JVM的执行引擎

解释执行器

即时编译器

JVM采用哪种方式?

即时编译器类型

JVM的分层编译5大级别:

分层编译级别:

热点代码:

如何找到热点代码?

java两大计数器:

OSR 编译(不重要,别纠结)

Code Cache

Code Cache的优化

Code Cache的查看

JDK9中的分段代码缓存:

AOT和Graal VM

Graal VM

重新认知JVM

运行时优化

方法内联

为什么会出现方法内联呢?

内联条件

逃逸分析

什么是“对象逃逸”?

什么是逃逸分析?

基于逃逸分析的优化

标量替换

栈上分配案例:

同步锁消除

什么条件下会触发逃逸分析?

TLAB(Thread Local Allocation Buffer)

TLAB分配的对象可以共享吗?


JVM内部的优化逻辑

JVM的执行引擎

javac编译器将Person.java源码文件编译成class文件[前期编译],交给JVM运行,因为JVM只能识别class字节码文件。同时在不同的操作系统上安装对应版本的JDK,里面包含了各自屏蔽操作系统底层细节的JVM,这样使得同一份class文件就能运行在不同的操作系统平台之上。这也是Write Once,Run Anywhere的原因所在。

最终JVM需要把字节码指令转换为机器码,可以理解为是0101这样的机器语言,这样才能运行在不同的机器上,那么由字节码转变为机器码是谁来做的呢?即谁来执行这些字节码指令的呢?这就是执行引擎

解释执行器

Interpreter解释器逐条把字节码翻译成机器码并执行,跨平台的保证。

刚开始执行引擎只采用了解释执行的,但是后来发现某些方法或者代码块被调用执行的特别频繁时,就会把这些代码认定为“热点代码”。

即时编译器

Just-In-Time compliation(JIT),即时编译器先将字节码编译成对应平台的可执行文件,运行速度快。即时编译器会把这些热点代码编译成与本地平台关联的机器码,并且进行各层次的优化,保存到内存中。

JVM采用哪种方式?

JVM采取的是混合模式,也就是解释+编译的方式,对于大部分不常用的代码,不需要浪费时间将其编译成机器码,只需要用到的时候再以解释的方式运行;对于小部分的热点代码,可以采取编译的方式,追求更高的运行效率。

即时编译器类型

(1)HotSpot虚拟机里面内置了两个JIT:C1和C2

C1也称为Client Compiler,适用于执行时间短或者对启动性能有要求的程序

C2也称为Server Compiler,适用于执行时间长或者对峰值性能有要求的程序

(2)Java7开始,HotSpot会使用分层编译的方式

分层编译也就是会结合C1的启动性能优势和C2的峰值性能优势,热点方法会先被C1编译,然后热点方法中的热点会被C2再次编译

-XX:+TieredCompilation开启参数

JVM的分层编译5大级别:

0.解释执行

1.简单的C1编译:仅仅使用我们的C1做一些简单的优化,不会开启Profiling

2.受限的C1编译代码:只会执行我们的方法调用次数以及循环的回边次数(多次执行的循环体)Profiling的C1编译

3.完全C1编译代码:我们Profiling里面所有的代码。也会被C1执行

4.C2编译代码:这个才是优化的级别。

级别越高,我们的应用启动越慢,优化下来开销会越高,同样的,我们的峰值性能也会越高

通常C2 代码的执行效率要比 C1 代码的高出 30% 以上

分层编译级别:

Java 虚拟机内置了 profiling。

profiling 是指在程序执行过程中,收集能够反映程序执行状态的数据。这里所收集的数据我们称之为程序的 profile。

如果方法的字节码数目比较少(如 getter/setter),而且 3 层的 profiling 没有可收集的数据。

那么,Java 虚拟机断定该方法对于 C1 代码和 C2 代码的执行效率相同。

在这种情况下,Java 虚拟机会在 3 层编译之后,直接选择用 1 层的 C1 编译。(简单的C1编译)

由于这是一个终止状态,因此 Java 虚拟机不会继续用 4 层的 C2 编译。

在 C1 忙碌的情况下,Java 虚拟机在解释执行过程中对程序进行 profiling,而后直接由 4 层的 C2 编译。

在 C2 忙碌的情况下,方法会被 2 层的 C1 编译,然后再被 3 层的 C1 编译,以减少方法在 3 层的执行时间。

Java 8 默认开启了分层编译。-XX:+TieredCompilation开启参数

不管是开启还是关闭分层编译,原本用来选择即时编译器的参数 -client-server 都是无效的。当关闭分层编译的情况下,Java 虚拟机将直接采用 C2。

如果你希望只是用 C1,那么你可以在打开分层编译的情况下使用参数 -XX:TieredStopAtLevel=1。在这种情况下,Java 虚拟机会在解释执行之后直接由 1 层的 C1 进行编译。

热点代码:

在运行过程中会被即时编译的“热点代码” 有两类,即:

  • 被多次调用的方法

  • 被多次执行的循环体

对于第一种,编译器会将整个方法作为编译对象,这也是标准的JIT 编译方式。对于第二种是由循环体出发的,但是编译器依然会以整个方法(而不是单独的循环体)作为编译对象,因为发生在方法执行过程中,称为栈上替换(On Stack Replacement,简称为 OSR 编译,即方法栈帧还在栈上,方法就被替换了)。

如何找到热点代码?

判断一段代码是否是热点代码,是不是需要触发即时编译,这样的行为称为热点探测(Hot Spot Detection),探测算法有两种,分别如下:

  • 基于采样的热点探测(Sample Based Hot Spot Detection):虚拟机会周期的对各个线程栈顶进行检查,如果某些方法经常出现在栈顶,这个方法就是“热点方法”。好处是实现简单、高效,很容易获取方法调用关系。缺点是很难确认方法的 reduce,容易受到线程阻塞或其他外因扰乱。

  • 基于计数器的热点探测(Counter Based Hot Spot Detection):为每个方法(甚至是代码块)建立计数器,执行次数超过阈值就认为是“热点方法”。优点是统计结果精确严谨。缺点是实现麻烦,不能直接获取方法的调用关系。

HotSpot 使用的是第二种——基于计数器的热点探测,并且有两类计数器:方法调用计数器(Invocation Counter )和回边计数器(Back Edge Counter )。

这两个计数器都有一个确定的阈值,超过后便会触发 JIT 编译。

java两大计数器:

(1)首先是方法调用计数器 。Client 模式下默认阈值是 1500 次,在 Server 模式下是 10000次,这个阈值可以通过 -XX:CompileThreadhold 来人为设定。如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间之内的方法被调用的次数。当超过一定的时间限度,如果方法的调用次数仍然不足以让它提交给即时编译器编译,那么这个方法的调用计数器就会被减少一半,这个过程称为方法调用计数器热度的衰减(Counter Decay),而这段时间就成为此方法的统计的半衰周期( Counter Half Life Time)。进行热度衰减的动作是在虚拟机进行垃圾收集时顺便进行的,可以使用虚拟机参数 -XX:CounterHalfLifeTime 参数设置半衰周期的时间,单位是秒。整个 JIT 编译的交互过程如下图。

(2)第二个回边计数器 ,作用是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”( Back Edge )。显然,建立回边计数器统计的目的就是为了触发 OSR 编译。

关于这个计数器的阈值, HotSpot 提供了 -XX:BackEdgeThreshold 供用户设置,但是当前的虚拟机实际上使用了 -XX:OnStackReplacePercentage 来简介调整阈值,计算公式如下:

  • Client 模式下, 公式为 方法调用计数器阈值(CompileThreshold)X OSR 比率(OnStackReplacePercentage)/ 100 。其中 OSR 比率默认为 933,那么,回边计数器的阈值为 13995

  • Server 模式下,公式为 方法调用计数器阈值(Compile Threashold)X (OSR 比率(OnStackReplacePercentage) - 解释器监控比率(InterpreterProfilePercent))/100。 其中 onStackReplacePercentage 默认值为 140,InterpreterProfilePercentage 默认值为 33,如果都取默认值,那么 Server 模式虚拟机回边计数器阈值为 10700

与方法计数器不同,回边计数器没有计数热度衰减的过程,因此这个计数器统计的就是该方法循环执行的绝对次数。当计数器溢出的时候,它还会把方法计数器的值也调整到溢出状态,这样下次再进入该方法的时候就会执行标准编译过程。

可以看到,决定一个方法是否为热点代码的因素有两个:方法的调用次数、循环回边的执行次数。即时编译便是根据这两个计数器的和来触发的。为什么 Java 虚拟机需要维护两个不同的计数器呢?

OSR 编译(不重要,别纠结)

实际上,除了以方法为单位的即时编译之外,Java 虚拟机还存在着另一种以循环为单位的即时编译,叫做 On-Stack-Replacement(OSR)编译。循环回边计数器便是用来触发这种类型的编译的。

OSR 实际上是一种技术,它指的是在程序执行过程中,动态地替换掉 Java 方法栈桢,从而使得程序能够在非方法入口处进行解释执行和编译后的代码之间的切换。也就是说,我只要遇到回边指令,我就可以触发执行切换。

在不启用分层编译的情况下,触发 OSR 编译的阈值是由参数 -XX:CompileThreshold 指定的阈值的倍数。

该倍数的计算方法为:

(OnStackReplacePercentage - InterpreterProfilePercentage)/100

其中 -XX:InterpreterProfilePercentage 的默认值为 33,当使用 C1 时 -XX:OnStackReplacePercentage 为 933,当使用 C2 时为 140。

也就是说,默认情况下,C1 的 OSR 编译的阈值为 13500,而 C2 的为 10700。

在启用分层编译的情况下,触发 OSR 编译的阈值则是由参数 -XX:TierXBackEdgeThreshold 指定的阈值乘以系数。

OSR 编译在正常的应用程序中并不多见。它只在基准测试时比较常见,因此并不需要过多了解。

那么这些即时编译器编译后的代码放哪呢?

Code Cache

JVM生成的native code存放的内存空间称之为Code Cache;JIT编译、JNI等都会编译代码到native code,其中JIT生成的native code占用了Code Cache的绝大部分空间,他是属于非堆内存的。

简而言之,JVM Code Cache (代码缓存)是JVM存储编译成本机代码的字节码的区域。我们将可执行本机代码的每个块称为 nmethodnmethod 可能是一个完整的或内联的Java方法。

即时( JIT )编译器是代码缓存区的最大消费者。这就是为什么一些开发人员将此内存称为JIT代码缓存。

Code Cache的优化

代码缓存的大小是固定的。一旦它满了,JVM就不会编译任何额外的代码,因为JIT编译器现在处于关闭状态。此外,我们将收到“ CodeCache is full… The compiler has been disabled ”警告消息。因此,我们的应用程序的性能最终会下降。为了避免这种情况,我们可以使用以下大小选项调整代码缓存:

  • InitialCodeCacheSize –初始代码缓存大小,默认为160K

  • ReservedCodeCacheSize –默认最大大小为48MB

  • CodeCacheExpansionSize –代码缓存的扩展大小,32KB或64KB

增加ReservedCodeCacheSize可能是一个解决方案,但这通常只是一个临时解决办法。

幸运的是,JVM提供了一个 UseCodeCache 刷新选项来控制代码缓存区域的刷新。其默认值为false。当我们启用它时,它会在满足以下条件时释放占用的区域:

  • 代码缓存已满;如果该区域的大小超过某个阈值,则会刷新该区域

  • 自上次清理以来已过了特定的时间间隔

  • 预编译代码不够热。对于每个编译的方法,JVM都会跟踪一个特殊的热度计数器。如果此计数器的值小于计算的阈值,JVM将释放这段预编译代码

Code Cache的查看

为了监控Code Cache(代码缓存)的使用情况,我们需要跟踪当前正在使用的内存的大小。

要获取有关代码缓存使用情况的信息,我们可以指定 –XX:+PrintCodeCache JVM选项。运行应用程序后,我们将看到类似的输出:

或者直接设置 -XX:ReservedCodeCacheSize=3000k,然后重启

让我们看看这些值的含义:

  • 输出中的大小显示内存的最大大小,与 ReservedCodeCacheSize 相同

  • used 是当前正在使用的内存的实际大小

  • max_used 是已使用的最大尺寸

  • free 是尚未占用的剩余内存

JDK9中的分段代码缓存:

从Java9开始,JVM将代码缓存分为三个不同的段,每个段都包含特定类型的编译代码。更具体地说,有三个部分:

-XX:nonNMethoddeHeapSize

-XX:ProfiledCodeHeapSize

-XX:nonprofiedCodeHeapSize

这种新结构以不同的方式处理各种类型的编译代码,从而提高了整体性能。

例如,将短命编译代码与长寿命代码分离可以提高方法清理器的性能——主要是因为它需要扫描更小的内存区域。

AOT和Graal VM

在Java9中,引入了AOT(Ahead-Of-Time)编译器

即时编译器是在程序运行过程中,将字节码翻译成机器码。而AOT是在程序运行之前,将字节码转换为机器码

优势:这样不需要在运行过程中消耗计算机资源来进行即时编译

劣势:AOT 编译无法得知程序运行时的信息,因此也无法进行基于类层次分析的完全虚方法内联,或者基于程序 profile 的投机性优化(并非硬性限制,我们可以通过限制运行范围,或者利用上一次运行的程序 profile 来绕开这两个限制)

Graal VM

官网: GraalVM | Oracle

GraalVM core features include:

  • GraalVM Native Image, available as an early access feature –– allows scripted applications to be compiled ahead of time into a native machine-code binary

  • GraalVM Compiler –– generates compiled code to run applications on a JVM, standalone, or embedded in another system

  • Polyglot Capabilities –– supports Java, Scala, Kotlin, JavaScript, and Node.js

  • Language Implementation Framework –– enables implementing any language for the GraalVM environment

  • LLVM Runtime–– permits native code to run in a managed environment in GraalVM Enterprise

在Java10中,新的JIT编译器Graal被引入

它是一个以Java为主要编程语言,面向字节码的编译器。跟C++实现的C1和C2相比,模块化更加明显,也更加容易维护。

Graal既可以作为动态编译器,在运行时编译热点方法;也可以作为静态编译器,实现AOT编译。

除此之外,它还移除了编程语言之间的边界,并且支持通过即时编译技术,将混杂了不同的编程语言的代码编译到同一段二进制码之中,从而实现不同语言之间的无缝切换。

重新认知JVM

JVM Architecture: Getting Started with the G1 Garbage Collector

运行时优化

方法内联

方法内联,是指 JVM在运行时将调用次数达到一定阈值的方法调用替换为方法体本身 ,从而消除调用成本,并为接下来进一步的代码性能优化提供基础,是JVM的一个重要优化手段之一。

注:

  • C++的inline属于编译后内联,但是java是运行时内联

简单通俗的讲就是把方法内部调用的其它方法的逻辑,嵌入到自身的方法中去,变成自身的一部分,之后不再调用该方法,从而节省调用函数带来的额外开支。

为什么会出现方法内联呢?

之所以出现方法内联是因为(方法调用)函数调用除了执行自身逻辑的开销外,还有一些不为人知的额外开销。 这部分额外的开销主要来自方法栈帧的生成、参数字段的压入、栈帧的弹出、还有指令执行地址的跳转 。比如有下面这样代码:

public static void function_A(int a, int b){//do somethingfunction_B(a,b);}public static void function_B(int c, int d){//do something}public static void main(String[] args){function_A(1,2);}

则代码的执行过程如下:

所以如果java中方法调用嵌套过多或者方法过多,这种额外的开销就越多。

试想一下想get/set这种方法调用:

public int getI() {return i;}public void setI(int i) {this.i = i;}

很可能自身执行逻辑的开销还比不上为了调用这个方法的额外开锁。如果类似的方法被频繁的调用,则真正相对执行效率就会很低,虽然这类方法的执行时间很短。这也是为什么jvm会在热点代码中执行方法内联的原因,这样的话就可以省去调用调用函数带来的额外开支。这里举个内联的可能形式:

 public int  add(int a, int b , int c, int d){return add(a, b) + add(c, d);}public int add(int a, int b){return a + b;}

内联之后:

public int  add(int a, int b , int c, int d){return a + b + c + d;}

内联条件

一个方法如果满足以下条件就很可能被jvm内联。

  • 热点代码。 如果一个方法的执行频率很高就表示优化的潜在价值就越大。那代码执行多少次才能确定为热点代码?这是根据编译器的编译模式来决定的。如果是客户端编译模式则次数是1500,服务端编译模式是10000。次数的大小可以通过-XX:CompileThreshold来调整。

  • 方法体不能太大。jvm中被内联的方法会编译成机器码放在code cache中。如果方法体太大,则能缓存热点方法就少,反而会影响性能。热点方法小于325字节的时候,非热点代码35字节以下才会使用这种方式

  • 如果希望方法被内联, 尽量用private、static、final修饰 ,这样jvm可以直接内联。如果是public、protected修饰方法jvm则需要进行类型判断,因为这些方法可以被子类继承和覆盖,jvm需要判断内联究竟内联是父类还是其中某个子类的方法。

所以了解jvm方法内联机制之后,会有助于我们工作中写出能让jvm更容易优化的代码,有助于提升程序的性能。

逃逸分析

什么是“对象逃逸”?

对象逃逸的本质是对象指针的逃逸。

在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。通俗点讲,如果一个对象的指针被多个方法或者线程引用时,那么我们就称这个对象的指针(或对象)的逃逸(Escape)。

什么是逃逸分析?

逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。 逃逸分析(Escape Analysis)算是目前Java虚拟机中比较前沿的优化技术了。

注意:逃逸分析不是直接的优化手段,而是代码分析手段。

对象逃逸案例:

Xpublic User doSomething1() {User user1 = new User ();user1 .setId(1);user1 .setDesc("xxxxxxxx");// ......return user1 ;
}

对象未逃逸:

public void doSomething2() {User user2 = new User ();user2 .setId(2);user2 .setDesc("xxxxxxxx");// ...... 
}

基于逃逸分析的优化

当判断出对象不发生逃逸时,编译器可以使用逃逸分析的结果作一些代码优化

  • 栈上分配:将堆分配转化为栈分配。如果某个对象在子程序中被分配,并且指向该对象的指针永远不会逃逸,该对象就可以在分配在栈上,而不是在堆上。在的垃圾收集的语言中,这种优化可以降低垃圾收集器运行的频率。

  • 同步消除:如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步。

  • 分离对象或标量替换。如果某个对象的访问方式不要求该对象是一个连续的内存结构,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。

标量替换

标量:不可被进一步分解的量,而Java中基本数据类型就是标量(比如int,long等基本数据类型) 。

聚合量: 标量的对立就是可以被进一步分解的量,称之为聚合量。 在Java中对象就是可以被进一步分解的聚合量。

标量替换:通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。

栈上分配案例:

虚拟机参数:

-XX:+PrintGC -Xms5M -Xmn5M -XX:+DoEscapeAnalysis

-XX:+DoEscapeAnalysis表示开启逃逸分析,JDK8是默认开启的

-XX:+PrintGC 表示打印GC信息

-Xms5M -Xmn5M 设置JVM内存大小是5M

public static void main(String[] args){for(int i = 0; i < 5_000_000; i++){createObject();}
}public static void createObject(){new Object();
}

运行结果是没有GC。

把虚拟机参数改成 -XX:+PrintGC -Xms5M -Xmn5M -XX:-DoEscapeAnalysis。关闭逃逸分析得到结果的部分截图是,说明了进行了GC,并且次数还不少。

这说明了JVM在逃逸分析之后,将对象分配在了方法createObject()方法栈上。方法栈上的对象在方法执行完之后,栈桢弹出,对象就会自动回收。这样的话就不需要等内存满时再触发内存回收。这样的好处是程序内存回收效率高,并且GC频率也会减少,程序的性能就提高了。

同步锁消除

如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步

虚拟机配置参数:-XX:+PrintGC -Xms500M -Xmn500M -XX:+DoEscapeAnalysis。配置500M是保证不触发GC。

public static void main(String[] args){long start = System.currentTimeMillis();for(int i = 0; i < 5_000_000; i++){createObject();}System.out.println("cost = " + (System.currentTimeMillis() - start) + "ms");}public static void createObject(){synchronized (new Object()){}}

运行结果

cost = 6ms

把逃逸分析关掉:-XX:+PrintGC -Xms500M -Xmn500M -XX:-DoEscapeAnalysis

运行结果

cost = 270ms

说明了逃逸分析把锁消除了,并在性能上得到了很大的提升。这里说明一下Java的逃逸分析是方法级别的,因为JIT ( just in time )即时编译器的即时编译是方法级别。

什么条件下会触发逃逸分析?

对象会先尝试栈上分配,如果不能成功分配,那么就去TLAB,如果还不行,就判定当前的垃圾收集器悲观策略,可不可以直接进入老年代,最后才会进入Eden。

Java的逃逸分析只发在JIT的即时编译中,因为在启动前已经通过各种条件判断出来是否满足逃逸,通过上面的流程图也可以得知对象分配不一定在堆上,所以可知满足逃逸的条件如下,只要满足以下任何一种都会判断为逃逸。

一、对象被赋值给堆中对象的字段和类的静态变量。

二、对象被传进了不确定的代码中去运行。

对象逃逸的范围有:全局逃逸、参数逃逸、没有逃逸;

TLAB(Thread Local Allocation Buffer)

即线程本地分配缓存区,这是一个线程专用的内存分配区域。

由于对象一般会分配在堆上,而堆是全局共享的。因此在同一时间,可能会有多个线程在堆上申请空间。因此,每次对象分配都必须要进行同步(虚拟机采用CAS+失败重试的方式保证更新操作的原子性),而在竞争激烈的场合分配的效率又会进一步下降。JVM使用TLAB来避免多线程冲突,在给对象分配内存时,每个线程使用自己的TLAB,这样可以避免线程同步,提高了对象分配的效率。

每个线程会从Eden分配一大块空间,例如说100KB,作为自己的TLAB。这个start是TLAB的起始地址,end是TLAB的末尾,然后top是当前的分配指针。显然start <= top < end。

当一个Java线程在自己的TLAB中分配到尽头之后,再要分配就会出发一次“TLAB refill”,也就是说之前自己的TLAB就“不管了”(所有权交回给共享的Eden),然后重新从Eden里分配一块空间作为新的TLAB。所谓“不管了”并不是说就让旧TLAB里的对象直接死掉,而是把那块空间的控制权归还给普通的Eden,里面的对象该怎样还是怎样。通常情况下,在TLAB中分配多次才会填满TLAB、触发TLAB refill,这样使用TLAB分配就比直接从共享部分的Eden分配要均摊(amortized)了同步开销,于是提高了性能。其实很多关注多线程性能的malloc库实现也会使用类似的做法,例如TCMalloc。

到触发GC的时候,无论是minor GC还是full GC,要收集Eden的时候里面的空间无论是属于某个线程的TLAB还是不属于任何TLAB都一视同仁,把Eden当作一个整体来收集里面的对象——把活的对象拷贝到survivor space(或者直接晋升到Old Gen)。在GC结束之后,每个Java线程又会重新从Eden分配自己的TLAB。周而复始。

TLAB分配的对象可以共享吗?

答:只要是Heap上的对象,所有线程都是可以共享的,就看你有没有本事访问到了。在GC的时候只从root sets来扫描对象,而不管你到底在哪个TLAB中。

相关文章:

JVM性能调优的基础知识 | JVM内部优化与运行时优化

目录 JVM内部的优化逻辑 JVM的执行引擎 解释执行器 即时编译器 JVM采用哪种方式&#xff1f; 即时编译器类型 JVM的分层编译5大级别&#xff1a; 分层编译级别&#xff1a; 热点代码&#xff1a; 如何找到热点代码&#xff1f; java两大计数器&#xff1a; OSR 编译…...

云计算-容器云-部署jumpserver 版本2

应用部署&#xff1a;堡垒机部署 # 使用提供的软件包配置Yum源&#xff0c;通过地址将jumpserver.tar.gz软件包下载至Jumpserver节点的/root目录下 [rootjumpserver ~]# tar -zxvf jumpserver.tar.gz -C /opt/ [rootjumpserver ~]# cp /opt/local.repo /etc/yum.repos.d/ [roo…...

MSP430G2553驱动0.96英寸OLED(硬件iic)

1.前言 最近需要用MSP430单片机做一个大作业,需要用到OLED模块&#xff0c;在这里记录一下 本篇文章主要讲解MSP430硬件iic的配置和OLED函数的调用&#xff0c;不会详细讲解OLED显示原理(其实就是江科大的OLED模块如何移植到msp430上).OLED显示原理以及底层函数讲解请参考其他…...

同质化的旅游内核

湘西凤凰古城、北京非常有文艺氛围的方家胡同都在被改造翻新为现代的其他城市范式式的样式。 什么意思呢&#xff1f;很多古城的老房子&#xff0c;从外面看&#xff0c;很古老、很漂亮&#xff0c;但是进去以后&#xff0c;完全不是那么回事&#xff0c;整座房子已经被完全掏…...

2025年五一数学建模A题【支路车流量推测】原创论文讲解(含完整python代码)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2025年五一数学建模A题【支路车流量推测】完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 A题论文共104页&a…...

文章六:《循环神经网络(RNN)与自然语言处理》

文章6&#xff1a;循环神经网络&#xff08;RNN&#xff09;与自然语言处理——让AI学会"说人话" 引言&#xff1a;你的手机为什么能秒懂你&#xff1f; 当你说"我想看科幻片"时&#xff0c;AI助手能立刻推荐《星际穿越》&#xff0c;这背后是RNN在"…...

Redis总结及设置营业状态案例

Redis简介: rRedis服务开启与停止: 服务开启: 在Redis配置文件中输入cmd进入命令行输入redis-server redis-cli.exe -h -p&#xff1a;连接到redis服务 设置密码:在redis.windows.conf中找到requirepass 密码 服务停止&#xff1a; 在服务开启的界面按ctrlc Redis数据类…...

中科大:LLM几何推理数据生成

&#x1f4d6;标题&#xff1a;Enhancing the Geometric Problem-Solving Ability of Multimodal LLMs via Symbolic-Neural Integration &#x1f310;来源&#xff1a;arXiv, 2504.12773 &#x1f31f;摘要 &#x1f538;多模态大语言模型&#xff08;MLLM&#xff09;的最…...

AimRT从入门到精通 - 04RPC客户端和服务器

一、ROS中的service通信机制 服务通信也是ROS中一种极其常用的通信模式&#xff0c;服务通信是基于请求响应模式的&#xff0c;是一种应答机制。也即&#xff1a;一个节点A向另一个节点B发送请求&#xff0c;B接收处理请求并产生响应结果返回给A。比如如下场景&#xff1a; 机器…...

【Android】Intent

目录 一、什么是Intent 二、显式Intent 三、隐式Intent 四、复杂数据传递 五、跨应用权限管理 六、常见问题 一、什么是Intent 1. 跨组件通信桥梁 实现组件间通信&#xff08;Activity/Service/BroadcastReceiver&#xff09;封装操作指令与数据传输逻辑 目标组件启动…...

从0开始建立Github个人博客(hugoPaperMod)

从0开始建立Github个人博客(hugo&PaperMod) github提供给每个用户一个网址&#xff0c;用户可以建立自己的静态网站。 一、Hugo hugo是一个快速搭建网站的工具&#xff0c;由go语言编写。 1.安装hugo 到hugo的github标签页Tags gohugoio/hugo选择一个版本&#xff0c…...

Python集合全解析:从基础到高阶应用实战

一、集合核心特性与创建方法 1.1 集合的本质特征 Python集合&#xff08;Set&#xff09;是一种​​无序且元素唯一​​的容器类型&#xff0c;基于哈希表实现&#xff0c;具有以下核心特性&#xff1a; ​​唯一性​​&#xff1a;自动过滤重复元素​​无序性​​&#xff…...

Matlab自学笔记

一、我下载的是Matlab R2016a软件&#xff0c;打开界面如下&#xff1a; 二、如何调整字体大小&#xff0c;路径为&#xff1a;“主页”->“预设”->“字体”。 三、命令行窗口是直接进行交互式的&#xff0c;如下输入“3 5”&#xff0c;回车&#xff0c;就得到结果“…...

Python爬虫实战:获取好大夫在线各专业全国医院排行榜数据并分析,为患者就医做参考

一、引言 在当今医疗资源丰富但分布不均的背景下,患者在选择合适的心血管内科医院时面临诸多困难。好大夫在线提供的医院排行榜数据包含了医院排名、线上服务得分、患者评价得分等重要信息,对患者选择医院具有重要的参考价值。本研究通过爬取该排行榜数据,并进行深入分析,…...

多模态人工智能研究:视觉语言模型的过去、现在与未来

多模态人工智能研究&#xff1a;视觉语言模型的过去、现在与未来 1. 引言&#xff1a;定义多模态图景 多模态人工智能指的是旨在处理和整合来自多种数据类型或“模态”信息的人工智能系统&#xff0c;这些模态包括文本、图像、音频和视频等。与通常侧重于单一模态&#xff08;…...

DeepSeek+Excel:解锁办公效率新高度

目录 一、引言&#xff1a;Excel 遇上 DeepSeek二、认识 DeepSeek&#xff1a;大模型中的得力助手2.1 DeepSeek 的技术架构与原理2.2 DeepSeek 在办公场景中的独特优势 三、DeepSeek 与 Excel 结合的准备工作3.1 获取 DeepSeek API Key3.2 配置 Excel 环境 四、DeepSeekExcel 实…...

3033. 修改矩阵

​题目来源&#xff1a; leetcode题目&#xff1a;3033. 修改矩阵 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 获取每列的最大值后将-1替换即可。 解题代码&#xff1a; #python3 class Solution:def getMaxRow(matrix:List[List[int]])->List[int]:r…...

Android面试总结之jet pack模块化组件篇

一、ViewModel 深入问题 1. ViewModel 如何实现跨 Fragment 共享数据&#xff1f;其作用域是基于 Activity 还是 Fragment&#xff1f; 问题解析&#xff1a; ViewModel 的作用域由 ViewModelStoreOwner 决定。当 Activity 和其内部 Fragment 共享同一个 ViewModelStoreOwner…...

【无需docker】mac本地部署dify

环境安装准备 #安装 postgresql13 brew install postgresql13 #使用zsh的在全局添加postgresql命令集 echo export PATH"/usr/local/opt/postgresql13/bin:$PATH" >> ~/.zshrc # 使得zsh的配置修改生效 source ~/.zshrc # 启动postgresql brew services star…...

清洗数据集

将label在图片上画出来 按照第一行的属性分类 import os import cv2 import multiprocessing as mp from tqdm import tqdm# ---------- 路径配置 ---------- # IMAGE_DIR = r"C:\Users\31919\Desktop\datasets\13k_100drive_raw_with_hand\images\test" LABEL_DIR =…...

支持向量机(SVM)详解

引言 支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种强大的监督学习算法&#xff0c;主要用于分类和回归任务。其核心思想是找到一个最优的决策边界&#xff08;超平面&#xff09;&#xff0c;最大化不同类别之间的间隔&#xff08;Margin&#xff09…...

MIT XV6 - 1.2 Lab: Xv6 and Unix utilities - pingpong

接上文 MIT XV6 - 1.1 Lab: Xv6 and Unix utilities - user/_sleep 是什么&#xff1f;做什么&#xff1f; pingpong 不务正业了那么久(然而并没有&#xff0c;虽然还在探索sleep&#xff0c;但是教材我已经看完了前三章了)&#xff0c;让我们赶紧继续下去 在进行本实验之前请务…...

“淘宝闪购”提前4天全量,意味着什么?

4月30日推出&#xff0c;首日上线50个城市&#xff0c;既定5月6日推广至全国的“淘宝闪购”&#xff0c;突然在5月2日早上官宣&#xff0c;提前4天面向全国消费者全量开放。 这一系列节奏&#xff0c;剑指一个字“快”&#xff01; 是业务发展远超预期的“快”。 4月30日&am…...

Servlet 解决了什么问题?

Servlet 主要解决了以下几个核心问题&#xff1a; 性能问题 (Performance): CGI 的问题&#xff1a; 传统的 CGI 技术为每个Web 请求都启动一个新的进程。进程的创建和销毁涉及大量的系统资源开销&#xff08;内存分配、CPU 时间、进程上下文切换等&#xff09;。在高并发场景下…...

Cherry Studio的MCP协议集成与应用实践:从本地工具到云端服务的智能交互

Cherry Studio的MCP协议集成与应用实践&#xff1a;从本地工具到云端服务的智能交互 一、MCP协议与Cherry Studio的技术融合 MCP&#xff08;Model Context Protocol&#xff09; 是由Anthropic提出的标准化协议&#xff0c;旨在为AI模型提供与外部工具交互的通用接口。通过M…...

CPU:AMD的线程撕裂者(Threadripper)系列

AMD的线程撕裂者&#xff08;Threadripper&#xff09;系列是AMD面向高性能计算&#xff08;HPC&#xff09;、工作站&#xff08;Workstation&#xff09;和高端桌面&#xff08;HEDT&#xff09;市场推出的顶级处理器产品线。该系列以极高的核心数、强大的多线程性能、丰富的…...

(即插即用模块-Attention部分) 六十二、(2022) LKA 大核注意力

文章目录 1、Larger Kernel Attention2、代码实现 paper&#xff1a;Visual Attention Network Code&#xff1a;https://github.com/Visual-Attention-Network 1、Larger Kernel Attention 自注意力机制在 NLP 领域取得了巨大成功&#xff0c;但其应用于计算机视觉任务时存在…...

Spring 分批处理 + 冷热数据分离:历史订单高效迁移与数据清理实战

在实际业务中&#xff0c;随着时间推移&#xff0c;订单量持续增长&#xff0c;若未及时进行数据治理&#xff0c;会造成数据库膨胀、查询缓慢、性能下降等问题。为了实现数据分层管理和系统高性能运行&#xff0c;我们在项目中采用了“冷热数据分离 分批迁移 数据清理”的综…...

Mybatis中的一级二级缓存扫盲

思维导图&#xff1a; MyBatis 提供了一级缓存和二级缓存机制&#xff0c;用于提高数据库查询的性能&#xff0c;减少对数据库的访问次数。&#xff08;本质上是减少IO次数&#xff09;。 一级缓存 1. 概念 一级缓存也称为会话缓存&#xff0c;它是基于 SqlSession 的缓存。在同…...

Elasticsearch 常用的 API 接口

文档类 API Index API &#xff1a;创建并建立索引&#xff0c;向指定索引添加文档。例如&#xff1a;PUT /twitter/tweet/1 &#xff0c;添加一个文档。 Get API &#xff1a;获取文档&#xff0c;通过索引、类型和 ID 获取文档。如GET /twitter/tweet/1。 DELETE API &…...

纯前端专业PDF在线浏览器查看器工具

纯前端专业PDF在线浏览器查看器工具 工具简介 我们最新开发的PDF在线浏览器工具现已发布&#xff01;这是一个基于Web的轻量级PDF阅读器&#xff0c;无需安装任何软件&#xff0c;直接在浏览器中即可查看和操作PDF文档。 主要功能 ✅ PDF文件浏览 支持本地PDF文件上传流畅的…...

传奇各职业/战士/法师/道士手套/手镯/护腕/神秘腰带爆率及出处产出地/圣战/法神/天尊/祈祷/虹魔/魔血

护腕排行(战士): 名字攻击攻击(均)魔法魔法(均)道术道术(均)防御防御(均)魔御魔御(均)重量要求图标外观产出圣战手镯2-32.50-000-000-10.50-002攻击: 400.02%双头金刚(50级/5000血/不死系)|赤月魔穴(1725,2125)60分钟2只 0.02%双头血魔(55级/5000血/不死系)|赤月魔穴(1725,212…...

觅知解析计费系统重构版在线支付卡密充值多解析接口免授权无后门源码扶风二开

一、源码描述 这是一套视频解析计费源码&#xff08;扶风二开&#xff09;&#xff0c;可配置多接口和专用特征解析接口&#xff0c;对接在线支付和卡密支付&#xff0c;支持在线充值和卡密充值&#xff0c;支持点数收费模式和包月套餐收费模式&#xff0c;可配置多个视频解析…...

C++11新特性_委托构造函数

格式定义 在 C11 里&#xff0c;委托构造函数的格式为&#xff1a;一个构造函数能够在其成员初始化列表里调用同一个类的其他构造函数。基本语法如下&#xff1a; class ClassName { public:// 被委托的构造函数&#xff08;目标构造函数&#xff09;ClassName(参数列表1) : …...

网工_IP协议

2025.02.17&#xff1a;小猿网&网工老姜学习笔记 第19节 IP协议 9.1 IP数据包的格式&#xff08;首部数据部分&#xff09;9.1.1 IP协议的首部格式&#xff08;固定部分可变部分&#xff09; 9.2 IP数据包分片&#xff08;找题练&#xff09;9.3 TTL生存时间的应用9.4 常见…...

C++负载均衡远程调用学习之QPS性能测试

目录 1.昨日回顾 2.QPS_TEST_PROTOBUF协议的集成 3.QPS_TEST_SERVER端实现 4.QPS_TEST_QPS简单介绍 5.QPS_TEST_QPS客户端工具编写和性能测试 1.昨日回顾 2.QPS_TEST_PROTOBUF协议的集成 ## 14) Reactor框架QPS性能测试 ​ 接下来我们写一个测试用例来测一下我们…...

C++负载均衡远程调用学习之消息队列与线程池

目录 1.昨日回顾 2.单线程的多路IO服务器模型和多线程模型区别 3.服务器的集中并发模式 4.LARSV0.8-task_msg消息队列任务数据类型 5.LARSV0.8--thread_queue消息队列的发送和接收流 6.LARSV0.8-thread_pool线程池的实现 7.LARSV0.8-thread_pool线程池的实现 8.LARSV0.8…...

Kotlin 基础

Kotlin基础语法详解 Kotlin是一种现代静态类型编程语言,由JetBrains开发,与Java完全互操作。以下是Kotlin的基础语法详解: 1. 基本语法 1.1 变量声明 // 不可变变量(推荐) val name: String = "Kotlin" val age = 25 // 类型推断// 可变变量 var count: In…...

实验数据的转换

最近做实验需要把x轴y轴z轴的数据处理一下&#xff0c;总结一下解决的方法&#xff1a; 源文件为两个txt文档&#xff0c;分别为x轴和y轴&#xff0c;如下&#xff1a; 最终需要达到的效果是如下&#xff1a; 就是需要把各个矩阵的数据整理好放在同一个txt文档里。 步骤① …...

多种尝试解决Pycharm无法粘贴外部文本【本人问题已解决】

#作者&#xff1a;允砸儿 #日期&#xff1a;乙巳青蛇年 四月初五 笔者在写demo的时候遇到一个非常棘手的问题就是pycharm无法复制粘贴&#xff0c;笔者相信有很多的朋友遇到过这种问题&#xff0c;笔者结合搜素到的和自己揣摩出来的方法帮助朋友们解决这种问题。 1、第一种…...

【C++】红黑树迭代版

目录 前言&#xff1a; 一&#xff1a;什么是红黑树&#xff1f; 二&#xff1a;插入什么颜色节点&#xff1f; 三&#xff1a;定义树 四&#xff1a;左单旋和右单旋 1.右单旋 2.左单旋 五&#xff1a;调整树 1.当parent节点为黑色时 2.当parent节点为红色时 2.1 u…...

OSPF路由协议配置

初始环境与准备: 物理连接:按照文件的拓扑连接了 3 台路由器 (R01, R02, R03)、2 台交换机 (Switch0, Switch1) 和 2 台 PC (PC0, PC1)。关键发现&#xff1a;路由器之间的连接实际使用的是以太网线&#xff08;连接到 FastEthernet 接口&#xff09;&#xff0c;而不是串口线。…...

linux下抓包工具--tcpdump介绍

文章目录 1. 前言2. 命令介绍3. 常见选项3.1. 接口与基本控制3.2 输出控制3.3 文件操作3.4 高级调试 4. 过滤表达式4.1 协议类型4.2 方向与地址4.3 逻辑运算符 5. 典型使用场景5.1 网络故障排查5.2 安全分析与入侵检测5.3 性能分析与优化 linux下抓包工具--tcpdump介绍 1. 前言…...

探索 Disruptor:高性能并发框架的奥秘

在当今的软件开发领域&#xff0c;处理高并发场景是一项极具挑战性的任务。传统的并发解决方案&#xff0c;如基于锁的队列&#xff0c;往往在高负载下表现出性能瓶颈。而 Disruptor 作为一个高性能的并发框架&#xff0c;凭借其独特的设计和先进的技术&#xff0c;在处理海量数…...

smss源代码分析之smss!SmpLoadSubSystemsForMuSession函数分析加载csrss.exe

第一部分&#xff1a; Next SmpSubSystemsToLoad.Flink; while ( Next ! &SmpSubSystemsToLoad ) { p CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry )…...

《AI大模型应知应会100篇》第44篇:大模型API调用最佳实践(附完整代码模板)

第44篇&#xff1a;大模型API调用最佳实践&#xff08;附完整代码模板&#xff09; 摘要 当你的应用突然面临每秒1000请求时&#xff0c;如何保证大模型API调用既稳定又经济&#xff1f;本文通过12个实战代码片段、3套生产级架构方案和20优化技巧&#xff0c;带你构建高性能的…...

第5篇:EggJS中间件开发与实战应用

在Web开发中&#xff0c;中间件&#xff08;Middleware&#xff09;是处理HTTP请求和响应的核心机制之一。EggJS基于Koa的洋葱模型实现了高效的中间件机制&#xff0c;本文将深入探讨中间件的执行原理、开发实践以及常见问题解决方案。 一、中间件执行机制与洋葱模型 1. 洋葱模…...

数字智慧方案6187丨智慧应急指挥平台体系建设方案(78页PPT)(文末有下载方式)

数字智慧方案6187丨智慧应急指挥平台体系建设方案 详细资料请看本解读文章的最后内容。 引言 随着社会经济的快速发展&#xff0c;应急管理面临着越来越复杂的挑战。智慧应急指挥平台体系的建设&#xff0c;旨在通过先进的信息技术和智能化手段&#xff0c;提升应急管理的效…...

Linux 常用命令 - tar【归档与压缩】

简介 tar 这个名称来源于 “tape archive”&#xff0c;最初设计用于将文件归档到磁带上。现在&#xff0c;tar 命令已经成为 Linux 系统中最常用的归档工具&#xff0c;它可以将多个文件和目录打包成一个单独的归档文件&#xff0c;并且可以选择使用不同的压缩算法进行压缩&a…...

python常用科学计算库及使用示例

​一、NumPy - 数值计算基础库​​ ​​安装​​ pip install numpy ​​核心功能示例​​ 1. 数组创建与运算 import numpy as np# 创建数组 arr np.array([1, 2, 3, 4]) matrix np.array([[1, 2], [3, 4]])# 数学运算 print(arr 1) # [2 3 4 5] print(matrix …...