不要再用main方法测试代码性能了,用这款JDK自带工具

前言

作为软件开发人员,我们通常会写一些测试程序用来对比不同算法、不同工具的性能问题。而最常见的做法是写一个main方法,构造模拟场景进行并发测试。

如果细心的朋友可能已经发现,每次测试结果误差很大,有时候测试出的结果甚至与事实相反。当然,这不排除是因为软硬件环境因素导致,但更多的可能是因为所使用测试方法自身有问题。

比如,不同需要性能比较方法放到一个虚拟机里调用,有可能会互相影响,缺少预热的过程等。

本文给大家推荐一款JDK9及以后自带的一款可用于软件基准测试的工具JMH(Java Microbenchmark Harness)。

JMH简介

JMH是用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。

何谓Micro Benchmark呢?简单的来说就是基于方法层面的基准测试,精度可以达到微秒级。当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用JMH对优化的结果进行量化的分析。

这款工具是由Oracle内部实现JIT的作者所写。我们知道JIT(Java即时编译器)是将JVM优化的所有高效手段和技术都使用上的地方。可想而知,开发者比任何人都更加了解JVM和JIT对基准测试的影响。

因此,这款工具是值得我们信赖和在实践中进行使用的。而且使用起来也非常方便。

使用场景

JMH不仅能帮我们测试一些常见类的性能,比如对比StringBuffer和StringBuilder的性能、对比不同算法的在不同数据量的性能等,还能够帮助我们对系统中发现的热点代码进行量化分析。

JMH通常用于以下应用场景:

  • 测试某个方法在稳定执行的情况下所需时间,以及执行时间和问题规模的相关性;

  • 对比接口不同实现在给定条件下的吞吐量

  • 查看多少百分比的请求在多长时间内完成

使用实例

依赖引入

如果你使用的是JDK9或以上版本,则JDK中已经自带了该工具,直接使用即可。如果你使用的是其他版本则可以通过maven直接引入以下依赖:

<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>1.27</version>
</dependency>
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-generator-annprocess</artifactId><version>1.27</version>
</dependency>

其中1.27是当前的最新版本,可根据实际需要更新或降低版本。

测试案例

下面以StringBuffer和StringBuilder的性能测试对比为例来进行基准测试。

//使用模式 默认是Mode.Throughput
@BenchmarkMode(Mode.AverageTime)
// 配置预热次数,默认是每次运行1秒,运行10次,这里设置为3次
@Warmup(iterations = 3, time = 1)
// 本例是一次运行4秒,总共运行3次,在性能对比时候,采用默认1秒即可
@Measurement(iterations = 3, time = 4)
// 配置同时起多少个线程执行
@Threads(1)
//代表启动多个单独的进程分别测试每个方法,这里指定为每个方法启动一个进程
@Fork(1)
// 定义类实例的生命周期,Scope.Benchmark:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能
@State(value = Scope.Benchmark)
// 统计结果的时间单元
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JmhTest {@Param(value = {"10", "50", "100"})private int length;public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(JmhTest.class.getSimpleName()).result("result.json").resultFormat(ResultFormatType.JSON).build();new Runner(opt).run();}@Benchmarkpublic void testStringBufferAdd(Blackhole blackhole) {StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {sb.append(i);}blackhole.consume(sb.toString());}@Benchmarkpublic void testStringBuilderAdd(Blackhole blackhole) {StringBuilder sb = new StringBuilder();for (int i = 0; i < length; i++) {sb.append(i);}blackhole.consume(sb.toString());}
}

上面介绍概念时已经提到Benchmark为基准测试,在使用中只需对要测试的方法添加@Benchmark注解即可。而在测试类JmhTest指定测试的预热、线程、测试维度等信息。

main方法中通过OptionsBuilder构造测试配置对象Options,并传入Runner,启动测试。这里指定测试结果为json格式,同时会将结果存储在result.json文件当中。

执行测试

执行main方法,控制台首先会打印出如下信息:

# JMH version: 1.27
# VM version: JDK 1.8.0_271, Java HotSpot(TM) 64-Bit Server VM, 25.271-b09
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=56800:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# JMH blackhole mode: full blackhole + dont-inline hint
# Warmup: 3 iterations, 1 s each
# Measurement: 3 iterations, 4 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.choupangxia.strings.JmhTest.testStringBufferAdd
# Parameters: (length = 10)

这些信息主要用来展示测试的基本信息,包括jdk、JVM、预热配置、执行轮次、执行时间、执行线程、测试的统计单位等。

# Warmup Iteration   1: 76.124 ns/op
# Warmup Iteration   2: 77.703 ns/op
# Warmup Iteration   3: 249.515 ns/op

这是对待测试方法的预热处理,这部分不会记入测试结果。预热主要让JVM对被测代码进行足够多的优化,比如JIT编译器的优化。

Iteration   1: 921.191 ns/op
Iteration   2: 897.729 ns/op
Iteration   3: 890.245 ns/opResult "com.choupangxia.strings.JmhTest.testStringBuilderAdd":903.055 ±(99.9%) 294.557 ns/op [Average](min, avg, max) = (890.245, 903.055, 921.191), stdev = 16.146CI (99.9%): [608.498, 1197.612] (assumes normal distribution)

显示每次(共3次)迭代执行速率,最后进行统计。这里是对testStringBuilderAdd方法执行length为100的测试,通过 (min, avg, max) 三项可以看出最小时间、平均时间、最大时间的值,单位为ns。stdev显示的是误差时间。

通常情况下,我们只用看最后的结果即可:

Benchmark                     (length)  Mode  Cnt     Score      Error  Units
JmhTest.testStringBufferAdd               10  avgt    3    92.599 ±  105.019  ns/op
JmhTest.testStringBufferAdd               50  avgt    3   582.974 ±  580.536  ns/op
JmhTest.testStringBufferAdd              100  avgt    3  1131.460 ± 1109.380  ns/op
JmhTest.testStringBuilderAdd        10  avgt    3    76.072 ±    2.824  ns/op
JmhTest.testStringBuilderAdd        50  avgt    3   450.325 ±   14.271  ns/op
JmhTest.testStringBuilderAdd       100  avgt    3   903.055 ±  294.557  ns/op

看到上述结果我们可能会很吃惊,我们知道StringBuffer要比StringBuilder的性能低一些,但结果发现它们的之间的差别并不是很大。这是因为JIT编译器进行了优化,比如当JVM发现在测试当中StringBuffer并没有发生逃逸,于是就进行了锁消除操作。

常用注解

下面对JHM当中常用的注解进行说明,以便大家可以更精确的使用。

@BenchmarkMode

配置Mode选项,作用于类或者方法上,其value属性为Mode数组,可同时支持多种Mode,如:@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}),也可设为Mode.All,即全部执行一遍。

org.openjdk.jmh.annotations.Mode为枚举类,对应的源代码如下:

public enum Mode {Throughput("thrpt", "Throughput, ops/time"),AverageTime("avgt", "Average time, time/op"),SampleTime("sample", "Sampling time"),SingleShotTime("ss", "Single shot invocation time"),All("all", "All benchmark modes");// 省略其他内容
}

不同模式之间,测量的维度或测量的方式不同。目前JMH共有四种模式:

  • Throughput:整体吞吐量,例如“1秒内可以执行多少次调用”,单位为ops/time;

  • AverageTime:调用的平均时间,例如“每次调用平均耗时xxx毫秒”,单位为time/op;

  • SampleTime:随机取样,最后输出取样结果的分布,,例如“99%的调用在xxx毫秒以内,99.99%的调用在xxx毫秒以内”;

  • SingleShotTime:以上模式都是默认一次iteration是1s,只有SingleShotTime是只运行一次。往往同时把warmup次数设为0,用于测试冷启动时的性能;

  • All:上面的所有模式都执行一次;

@Warmup

在执行@Benchmark之前进行预热操作,确保测试的准确性,可用于类或者方法上。默认是每次运行1秒,运行10次。

其中@Warmup有以下属性:

  • iterations:预热的次数;Iteration是JMH进行测试的最小单位,在大部分模式下,一次iteration代表的是一秒,JMH会在这一秒内不断调用需要benchmark的方法,然后根据模式对其采样,计算吞吐量,计算平均执行时间等。

  • time:每次预热的时间;

  • timeUnit:时间的单位,默认秒;

  • batchSize:批处理大小,每次操作调用几次方法;

JIT在执行的过程中会将热点代码编译为机器码,并进行各种优化,从而提高执行效率。预热的主要目的是让JVM的JIT机制生效,让结果更接近真实效果。

@State

类注解,JMH测试类必须使用@State注解,不然会提示无法运行。

State定义了一个类实例的生命周期(作用范围),可以类比Spring Bean的Scope。因为很多benchmark会需要一些表示状态的类,JMH会根据scope来进行实例化和共享操作。

@State可以被继承使用,如果父类定义了该注解,子类则无需定义。

由于JMH允许多线程同时执行测试,不同的选项含义如下:

  • Scope.Thread:默认的State,该状态为每个线程独享,每个测试线程分配一个实例;

  • Scope.Benchmark:该状态在所有线程间共享,所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;

  • Scope.Group:该状态为同一个组里面所有线程共享。

@OutputTimeUnit

benchmark统计结果所使用的时间单位,可用于类或者方法注解,使用java.util.concurrent.TimeUnit中的标准时间单位。

@Measurement

度量,其实就是实际调用方法所需要配置的一些基本测试参数,可用于类或者方法上。配置属性项目和作用与@Warmup相同。

一般比较重的程序可以进行大量的测试,放到服务器上运行。在性能对比时,采用默认1秒即可,如果用jvisualvm做性能监控,可以指定一个较长时间运行。

@Threads

每个进程中同时起多少个线程执行,可用于类或者方法上。默认值是Runtime.getRuntime().availableProcessors(),根据具体情况选择,一般为cpu乘以2。

@Fork

代表启动多个单独的进程分别测试每个方法,可用于类或者方法上。如果fork数是2的话,则JMH会fork出两个进程来进行测试。

JVM因为使用了profile-guided optimization而“臭名昭著”,这对于微基准测试来说十分不友好,因为不同测试方法的profile混杂在一起,“互相伤害”彼此的测试结果。对于每个@Benchmark方法使用一个独立的进程可以解决这个问题,这也是JMH的默认选项。注意不要设置为0,设置为n则会启动n个进程执行测试(似乎也没有太大意义)。fork选项也可以通过方法注解以及启动参数来设置。

@Param

属性级注解,指定某项参数的多种情况,特别适合用来测试一个函数在不同的参数输入的情况下的性能,只能作用在字段上,使用该注解必须定义@State注解。

@Param注解接收一个String数组,在@Setup方法执行前转化为对应的数据类型。多个@Param注解的成员之间是乘积关系,譬如有两个用@Param注解的字段,第一个有5个值,第二个字段有2个值,那么每个测试方法会跑5*2=10次。

@Benchmark

方法注解,表示该方法是需要进行benchmark的对象,用法和JUnit的@Test类似。

@Setup

方法注解,这个注解的作用就是我们需要在测试之前进行一些准备工作,比如对一些数据的初始化之类的。

@TearDown

方法注解,与@Setup相对的,会在所有benchmark执行结束以后执行,比如关闭线程池,数据库连接等的,主要用于资源的回收等。

Threads

每个fork进程使用多少个线程去执行测试方法,默认值是Runtime.getRuntime().availableProcessors()。

@Group

方法注解,可以把多个benchmark定义为同一个group,则它们会被同时执行,譬如用来模拟生产者-消费者读写速度不一致情况下的表现。

@Level

用于控制@Setup,@TearDown的调用时机,默认是Level.Trial。

  • Trial:每个benchmark方法前后;

  • Iteration:每个benchmark方法每次迭代前后;

  • Invocation:每个benchmark方法每次调用前后,谨慎使用,需留意javadoc注释;

JMH注意事项

无用代码消除(Dead Code Elimination)

现代编译器是十分聪明的,它们会对代码进行推导分析,判定哪些代码是无用的然后进行去除,这种行为对微基准测试是致命的,它会使你无法准确测试出你的方法性能。

JMH本身已经对这种情况做了处理,要记住:1.永远不要写void方法;2.在方法结束返回计算结果。有时候如果需要返回多于一个结果,可以考虑自行合并计算结果,或者使用JMH提供的BlackHole对象:

/** This demonstrates Option A:** Merge multiple results into one and return it.* This is OK when is computation is relatively heavyweight, and merging* the results does not offset the results much.*/
@Benchmark
public double measureRight_1() {return Math.log(x1) + Math.log(x2);
}
/** This demonstrates Option B:** Use explicit Blackhole objects, and sink the values there.* (Background: Blackhole is just another @State object, bundled with JMH).*/
@Benchmark
public void measureRight_2(Blackhole bh) {bh.consume(Math.log(x1));bh.consume(Math.log(x2));
}

再比如下面代码:

@Benchmark
public void testStringAdd(Blackhole blackhole) {String a = "";for (int i = 0; i < length; i++) {a += i;}
}

JVM可能会认为变量a从来没有使用过,从而进行优化把整个方法内部代码移除掉,这就会影响测试结果。

JMH提供了两种方式避免这种问题,一种是将这个变量作为方法返回值return a,一种是通过Blackhole的consume来避免JIT 的优化消除。

常量折叠(Constant Folding)

常量折叠是一种现代编译器优化策略,例如,i = 320 * 200 * 32,多数的现代编译器不会真的产生两个乘法的指令再将结果储存下来,取而代之的,它们会辨识出语句的结构,并在编译时期将数值计算出来(i = 2,048,000)。

在微基准测试中,如果你的计算输入是可预测的,也不是一个@State实例变量,那么很可能会被JIT给优化掉。对此,JMH的建议是:1.永远从@State实例中读取你的方法输入;2.返回你的计算结果;3.或者考虑使用BlackHole对象;

见如下官方例子:

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_10_ConstantFold {private double x = Math.PI;private final double wrongX = Math.PI;@Benchmarkpublic double baseline() {// simply return the value, this is a baselinereturn Math.PI;}@Benchmarkpublic double measureWrong_1() {// This is wrong: the source is predictable, and computation is foldable.return Math.log(Math.PI);}@Benchmarkpublic double measureWrong_2() {// This is wrong: the source is predictable, and computation is foldable.return Math.log(wrongX);}@Benchmarkpublic double measureRight() {// This is correct: the source is not predictable.return Math.log(x);}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(JMHSample_10_ConstantFold.class.getSimpleName()).warmupIterations(5).measurementIterations(5).forks(1).build();new Runner(opt).run();}
}

循环展开(Loop Unwinding)

循环展开最常用来降低循环开销,为具有多个功能单元的处理器提供指令级并行。也有利于指令流水线的调度。例如:

for (i = 1; i <= 60; i++) a[i] = a[i] * b + c;

可以展开成:

for (i = 1; i <= 60; i+=3){a[i] = a[i] * b + c;a[i+1] = a[i+1] * b + c;a[i+2] = a[i+2] * b + c;
}

由于编译器可能会对你的代码进行循环展开,因此JMH建议不要在你的测试方法中写任何循环。如果确实需要执行循环计算,可以结合@BenchmarkMode(Mode.SingleShotTime)和@Measurement(batchSize = N)来达到同样的效果。参考如下例子:

/** Suppose we want to measure how much it takes to sum two integers:*/
int x = 1;
int y = 2;
/** This is what you do with JMH.*/
@Benchmark
@OperationsPerInvocation(100)
public int measureRight() {return (x + y);
}

JMH可视化

在示例的main方法中指定了生成测试结果的输出文件result.json,其中的内容就是控制台输出的相关内容以json格式存储。

针对json格式的内容,可以在其他网站上以图表的形式可视化展示。

对应网站,JMH Visual Chart(http://deepoove.com/jmh-visual-chart/)、JMH Visualizer(https://jmh.morethan.io/)。

展示效果如下图:

img

生成jar包执行

对于大型的测试,一般会放在Linux服务器里去执行。JMH官方提供了生成jar包的方式来执行,在maven里增加如下插件:

<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>2.4.1</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><finalName>jmh-demo</finalName><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>org.openjdk.jmh.Main</mainClass></transformer></transformers></configuration></execution></executions></plugin>
</plugins>

执行maven的命令生成可执行jar包,并执行:

mvn clean package
java -jar target/jmh-demo.jar JmhTest

总结

一篇文章几乎涵盖了JMH各方面的知识点,如果实践中还没运用,赶紧用起来吧,你的专业水平将又提升那么一点。当然,也可以收藏起来,以备不时不需。

参考文章:

https://www.zhihu.com/question/276455629/answer/1259967560 https://www.cnblogs.com/silyvin/p/11736696.html https://blog.csdn.net/wangxuelei036/article/details/105240522 https://www.cnblogs.com/xiang--liu/p/9710143.html


往期推荐

6种快速统计代码执行时间的方法,真香!(史上最全)


Oracle官方推荐的性能测试工具!简单、精准又直观!


链表竟然比数组慢了1000多倍?(动图+性能评测)


关注我,每天陪你进步一点点!

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

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

相关文章

读书总结:周鸿祎,我的互联网方法论

目录&#xff1a;1、欢迎来到互联网时代2、互联网用户至上3、颠覆式创新4、免费时代5、体验为王6、互联网方法论第一章&#xff1a;互联网时代1、没人能打败趋势&#xff0c;趋势会导致非线性发展。2、信息的流通变快&#xff0c;商家很难利用非对称性赚钱。3、用户的体验最重要…

Java ClassLoader getResources()方法与示例

ClassLoader类的getResources()方法 (ClassLoader Class getResources() method) getResources() method is available in java.lang package. getResources()方法在java.lang包中可用。 getResources() method is used to identify all the resources with the given resource…

Java中Properties类的操作

http://www.cnblogs.com/bakari/p/3562244.html Java中Properties类的操作 知识学而不用&#xff0c;就等于没用&#xff0c;到真正用到的时候还得重新再学。最近在看几款开源模拟器的源码&#xff0c;里面涉及到了很多关于Properties类的引用&#xff0c;由于Java已经好久没用…

复盘线上的一次OOM和性能优化!

来源&#xff1a;r6d.cn/ZazN上周五&#xff0c;发布前一周的服务器小动荡????事情回顾上周五&#xff0c;通过Grafana监控&#xff0c;线上环境突然出现CPU和内存飙升的情况&#xff1a;但是看到网络输入和输入流量都不是很高&#xff0c;所以网站被别人攻击的概率不高&am…

scanf 输入十六进制_在C语言中使用scanf()输入一个十六进制值

scanf 输入十六进制Here, we have to declare an unsigned int variable and input a value in hexadecimal format. 在这里&#xff0c;我们必须声明一个无符号的int变量&#xff0c;并以十六进制格式输入一个值。 To input a value in hexadecimal format – we use "%…

阅读源码的 4 个绝技,我必须分享给你!

为什么要阅读源码&#xff1f;1.在通用型基础技术中提高技术能力在 JAVA 领域中包含 JAVA 集合、Java并发(JUC)等&#xff0c; 它们是项目中使用的高频技术&#xff0c;在各种复杂的场景中选用合适的数据结构、线程并发模型&#xff0c;合理控制锁粒度等都能显著提高应用程序的…

微信公众号开发 ssl connect error

微信获取公众号授权失败 &#xff1a;ssl connect error 本人用的是微擎&#xff0c;也是刚入手&#xff0c;碰到这个问题感觉很棘手。 通过一步步调试发现问题出在curl 认证这里&#xff0c;得到结果错误代码&#xff1a;35&#xff0c;错误信息就是&#xff1a;ssl connect …

struts2的java.lang.NoSuchMethodException异常处理

不久前在学习struts时出现这个错误&#xff0c;在网上搜索了半天&#xff0c;发现答案不一。将其总结如下&#xff0c;以方便大家参考。 1、 你有没有试试看 其它的方法能不能用&#xff0c;要是都是这种情况的话&#xff0c;可能是你的Action类没有继承structs里面的DispatchA…

Java String indexOf(int ch)方法与示例

字符串indexOf(int ch)方法 (String indexOf(int ch) Method) indexOf(int ch) is a String method in Java and it is used to get the index of a specified character in the string. indexOf(int ch)是Java中的String方法&#xff0c;用于获取字符串中指定字符的索引。 If…

innerHTML、innerText和outerHTML、outerText的区别

1、区别描述如下&#xff1a; innerHTML 设置或获取位于对象起始和结束标签内的 HTMLouterHTML 设置或获取对象及其内容的 HTML 形式innerText 设置或获取位于对象起始和结束标签内的文本outerText 设置(包括标签)或获取(不包括标签)对象的文本innerText和outerText在获取时是相…

Socket粘包问题终极解决方案—Netty版(2W字)!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;上一篇我们写了《Socket粘包问题的3种解决方案》&#xff0c;但没想到评论区竟然炸了。介于大家的热情讨论&#xff0c;以及…

Java高质量代码之 — 泛型与反射

在Java5后推出了泛型,使我们在编译期间操作集合或类时更加的安全,更方便代码的阅读,而让身为编译性语言的Java提供动态性的反射技术,更是在框架开发中大行其道,从而让Java活起来,下面看一下在使用泛型和反射需要注意和了解的事情 1.Java的泛型是类型擦除的 Java中的泛型是…

Java LocalDate类| isLeapYear()方法与示例

LocalDate类isLeapYear()方法 (LocalDate Class isLeapYear() method) isLeapYear() method is available in java.time package. isLeapYear()方法在java.time包中可用。 isLeapYear() method is used to check whether the year field value is a leap year or not based on …

Redis 消息队列的三种方案(List、Streams、Pub/Sub)

现如今的互联网应用大都是采用 分布式系统架构 设计的&#xff0c;所以 消息队列 已经逐渐成为企业应用系统 内部通信 的核心手段&#xff0c;它具有 低耦合、可靠投递、广播、流量控制、最终一致性 等一系列功能。当前使用较多的 消息队列 有 RabbitMQ、RocketMQ、ActiveMQ、K…

JavaScript的求模、取整、小数的取舍

js 求模、整除 主要方法是参考JavaScript Math 对象&#xff0c;列举两个常用方法&#xff1b; floor(x)&#xff1a;对数进行下舍入。 round(x)&#xff1a;把数四舍五入为最接近的整数。 更详细的&#xff1a;http://www.w3school.com.cn/js/jsref_obj_math.asp <spa…

c struct 对齐_C中的struct大小| 填充,结构对齐

c struct 对齐What we know is that size of a struct is the sum of all the data members. Like for the following struct, 我们知道的是&#xff0c; 结构的大小是所有数据成员的总和 。 对于以下结构&#xff0c; struct A{int a;int* b;char c;char *d;};Size of the st…

超3000岗位!腾讯产业互联网新年大扩招!

虽然离春节仅剩 1 个月的时间&#xff0c;大厂依旧没有停止招人。就在上周&#xff0c;腾讯官宣新年大扩招&#xff0c;放出 3000 多个岗位需求&#xff01;我们查看了腾讯的招聘数据发现&#xff0c;除了大量招聘运营人员&#xff0c;你猜&#xff0c;他们还在批量招聘什么岗位…

js中的弹窗alert、confirm和prompt

转载&#xff1a;http://blog.csdn.net/cui_angel/article/details/7784211 alert() 弹出个提示框 &#xff08;确定&#xff09; confirm() 弹出个确认框 &#xff08;确定&#xff0c;取消&#xff09; prompt() 弹出个输入框 让你输入东西 使用消息框 使用警告、提示和…

骚操作,IDEA防止写代码沉迷插件 !

当初年少懵懂&#xff0c;那年夏天填志愿选专业&#xff0c;父母听其他长辈说选择计算机专业好。从那以后&#xff0c;我的身上就有了计院深深的烙印。从寝室到机房&#xff0c;从机房到图书馆&#xff0c;C、C、Java、只要是想写点自己感兴趣的东西&#xff0c;一坐就是几个小…

mcq 队列_MCQ | 基础知识 免费和开源软件| 套装3

mcq 队列Q1. What do you understand from GNOME and KDE? Q1。 您从GNOME和KDE了解到什么&#xff1f; Linux Distribution Linux发行版 Command Lines 指令行 GUI Based Linux 基于GUI的Linux File Framework 文件框架 Answer: c. GUI Based Linux 答&#xff1a; c。 基于…