Java 教程
- Java - 首页
- Java - 概述
- Java - 历史
- Java - 特性
- Java 与 C++
- JVM - Java 虚拟机
- Java - JDK 与 JRE 与 JVM
- Java - Hello World 程序
- Java - 环境设置
- Java - 基本语法
- Java - 变量类型
- Java - 数据类型
- Java - 类型转换
- Java - Unicode 系统
- Java - 基本运算符
- Java - 注释
- Java - 用户输入
- Java - 日期和时间
Java 控制语句
- Java - 循环控制
- Java - 决策制定
- Java - If-else
- Java - Switch
- Java - For 循环
- Java - For-Each 循环
- Java - While 循环
- Java - do-while 循环
- Java - Break
- Java - Continue
面向对象编程
- Java - OOPs 概念
- Java - 对象和类
- Java - 类属性
- Java - 类方法
- Java - 方法
- Java - 变量作用域
- Java - 构造函数
- Java - 访问修饰符
- Java - 继承
- Java - 聚合
- Java - 多态
- Java - 重写
- Java - 方法重载
- Java - 动态绑定
- Java - 静态绑定
- Java - 实例初始化块
- Java - 抽象
- Java - 封装
- Java - 接口
- Java - 包
- Java - 内部类
- Java - 静态类
- Java - 匿名类
- Java - 单例类
- Java - 包装类
- Java - 枚举
- Java - 枚举构造函数
- Java - 枚举字符串
Java 内置类
Java 文件处理
Java 错误和异常
- Java - 异常
- Java - try-catch 块
- Java - try-with-resources
- Java - 多重 catch 块
- Java - 嵌套 try 块
- Java - Finally 块
- Java - throw 异常
- Java - 异常传播
- Java - 内置异常
- Java - 自定义异常
Java 多线程
- Java - 多线程
- Java - 线程生命周期
- Java - 创建线程
- Java - 启动线程
- Java - 线程连接
- Java - 线程命名
- Java - 线程调度器
- Java - 线程池
- Java - 主线程
- Java - 线程优先级
- Java - 守护线程
- Java - 线程组
- Java - 关闭钩子
Java 同步
Java 网络
- Java - 网络
- Java - 套接字编程
- Java - URL 处理
- Java - URL 类
- Java - URLConnection 类
- Java - HttpURLConnection 类
- Java - Socket 类
- Java - 泛型
Java 集合
Java 接口
Java 数据结构
Java 集合算法
高级 Java
- Java - 命令行参数
- Java - Lambda 表达式
- Java - 发送邮件
- Java - Applet 基础
- Java - Javadoc 注释
- Java - 自动装箱和拆箱
- Java - 文件不匹配方法
- Java - REPL (JShell)
- Java - 多版本 Jar 文件
- Java - 私有接口方法
- Java - 内部类菱形运算符
- Java - 多分辨率图像 API
- Java - 集合工厂方法
- Java - 模块系统
- Java - Nashorn JavaScript
- Java - Optional 类
- Java - 方法引用
- Java - 函数式接口
- Java - 默认方法
- Java - Base64 编码解码
- Java - Switch 表达式
- Java - Teeing 收集器
- Java - 微基准测试
- Java - 文本块
- Java - 动态 CDS 归档
- Java - Z 垃圾收集器 (ZGC)
- Java - 空指针异常
- Java - 打包工具
- Java - 密封类
- Java - 记录类
- Java - 隐藏类
- Java - 模式匹配
- Java - 简洁数字格式化
- Java - 垃圾回收
- Java - JIT 编译器
Java 杂项
- Java - 递归
- Java - 正则表达式
- Java - 序列化
- Java - 字符串
- Java - Process API 改进
- Java - Stream API 改进
- Java - 增强的 @Deprecated 注释
- Java - CompletableFuture API 改进
- Java - 流
- Java - 日期时间 API
- Java 8 - 新特性
- Java 9 - 新特性
- Java 10 - 新特性
- Java 11 - 新特性
- Java 12 - 新特性
- Java 13 - 新特性
- Java 14 - 新特性
- Java 15 - 新特性
- Java 16 - 新特性
Java API 和框架
Java 类参考
- Java - Scanner
- Java - 数组
- Java - 字符串
- Java - Date
- Java - ArrayList
- Java - Vector
- Java - Stack
- Java - PriorityQueue
- Java - LinkedList
- Java - ArrayDeque
- Java - HashMap
- Java - LinkedHashMap
- Java - WeakHashMap
- Java - EnumMap
- Java - TreeMap
- Java - IdentityHashMap
- Java - HashSet
- Java - EnumSet
- Java - LinkedHashSet
- Java - TreeSet
- Java - BitSet
- Java - Dictionary
- Java - Hashtable
- Java - Properties
- Java - Collection
- Java - Array
Java 有用资源
Java - 微基准测试
Java 基准测试
基准测试是一种检查应用程序或应用程序代码部分性能的技术,以吞吐量、平均花费时间等为指标。在Java 基准测试中,我们检查应用程序代码或正在使用的库的性能。此基准测试有助于在遇到大量负载时识别代码中的任何瓶颈,或识别应用程序性能下降的情况。
Java 基准测试的重要性
应用程序性能是任何应用程序的一个非常重要的属性。编写不当的代码会导致应用程序无响应,以及高内存使用率,这可能导致糟糕的用户体验,甚至使应用程序无法使用。为了解决此类问题,对应用程序进行基准测试至关重要。
Java 开发人员应该能够找到应用程序中的问题并使用基准测试技术修复它们,这可以帮助识别缓慢的代码。
Java 基准测试技术
开发人员为了基准测试应用程序代码、系统、库或任何组件,部署了多种技术。以下是其中一些重要技术。
使用开始/结束时间进行基准测试
此技术易于使用,并且适用于一小段代码,我们可以在调用代码之前以纳秒为单位获取开始时间,然后在执行方法后,我们再次获取时间。现在,使用结束时间和开始时间的差值可以了解代码花费了多少时间。此技术很简单,但不可靠,因为性能会因许多因素而异,例如正在运行的垃圾收集器以及在此期间运行的任何系统进程。
// get the start time long startTime = System.nanoTime(); // execute the code to be benchmarked long result = operations.timeTakingProcess(); // get the end time long endTime = System.nanoTime(); // compute the time taken long timeElapsed = endTime - startTime;
示例
以下示例显示了一个运行示例,以演示上述概念。
package com.tutorialspoint; public class Operations { public static void main(String[] args) { Operations operations = new Operations(); // get the start time long startTime = System.nanoTime(); // execute the code to be benchmarked long result = operations.timeTakingProcess(); // get the end time long endTime = System.nanoTime(); // compute the time taken long timeElapsed = endTime - startTime; System.out.println("Sum of 100,00 natural numbers: " + result); System.out.println("Elapsed time: " + timeElapsed + " nanoseconds"); } // get the sum of first 100,000 natural numbers public long timeTakingProcess() { long sum = 0; for(int i = 0; i < 100000; i++ ) { sum += i; } return sum; } }
让我们编译并运行上述程序,这将产生以下结果 -
Sum of 100,00 natural numbers: 4999950000 Elapsed time: 1111300 nanoseconds
使用 Java 微基准测试工具 (JMH) 进行基准测试
Java 微基准测试工具 (JMH) 是由 OpenJDK 社区开发的一个功能强大的基准测试 API,用于检查代码的性能。它提供了一种简单的基于注释的方法来获取方法/类的基准数据,开发人员只需编写很少的代码。
步骤 1 - 注释要进行基准测试的类/方法。
@Benchmark public long timeTakingProcess() { }
步骤 2 - 准备基准测试选项,并使用基准测试运行器运行。
// prepare the options Options options = new OptionsBuilder() .include(Operations.class.getSimpleName()) // use the class whose method is to be benchmarked .forks(1) // create the fork(s) which will be used to run the iterations .build(); // run the benchmark runner new Runner(options).run();
为了使用基准测试库,我们需要在 Maven 项目的 pom.xml 中添加以下依赖项。
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.35</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.35</version> </dependency>
以下是用于运行上述基准测试示例的 pom.xml 的完整代码。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tutorialspoint</groupId> <artifactId>test</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>test</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jmh.version>1.35</jmh.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>${jmh.version}</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>${jmh.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>17</source> <target>17</target> <annotationProcessorPaths> <path> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>${jmh.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <finalName>benchmarks</finalName> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>org.openjdk.jmh.Main</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
示例
以下是用于运行上述基准测试示例的类的完整代码。
package com.tutorialspoint.test; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; public class Operations { public static void main(String[] args) throws RunnerException { Options options = new OptionsBuilder() .include(Operations.class.getSimpleName()) .forks(1) .build(); new Runner(options).run(); } // get the sum of first 100,000 natural numbers @Benchmark public long timeTakingProcess() { long sum = 0; for(int i = 0; i < 100000; i++ ) { sum += i; } return sum; } }
让我们编译并运行上述程序,这将产生以下结果 -
# JMH version: 1.35 # VM version: JDK 21.0.2, Java HotSpot(TM) 64-Bit Server VM, 21.0.2+13-LTS-58 # VM invoker: C:\Program Files\Java\jdk-21\bin\java.exe # VM options: -Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ShowCodeDetailsInExceptionMessages # Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable) # Warmup: 5 iterations, 10 s each # Measurement: 5 iterations, 10 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Throughput, ops/time # Benchmark: com.tutorialspoint.test.Operations.timeTakingProcess # Run progress: 0.00% complete, ETA 00:01:40 # Fork: 1 of 1 # Warmup Iteration 1: 33922.775 ops/s # Warmup Iteration 2: 34104.930 ops/s # Warmup Iteration 3: 34519.419 ops/s # Warmup Iteration 4: 34535.636 ops/s # Warmup Iteration 5: 34508.781 ops/s Iteration 1: 34497.252 ops/s Iteration 2: 34338.847 ops/s Iteration 3: 34355.355 ops/s Iteration 4: 34105.801 ops/s Iteration 5: 34104.127 ops/s Result "com.tutorialspoint.test.Operations.timeTakingProcess": 34280.276 ±(99.9%) 660.293 ops/s [Average] (min, avg, max) = (34104.127, 34280.276, 34497.252), stdev = 171.476 CI (99.9%): [33619.984, 34940.569] (assumes normal distribution) # Run complete. Total time: 00:01:40 REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise extra caution when trusting the results, look into the generated code to check the benchmark still works, and factor in a small probability of new VM bugs. Additionally, while comparisons between different JVMs are already problematic, the performance difference caused by different Blackhole modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons. Benchmark Mode Cnt Score Error Units Operations.timeTakingProcess thrpt 5 34280.276 ± 660.293 ops/s