引入
先看一段特别简单的Java代码:每次创建1万个对象并打印耗时,重复100次。
测试代码:
static final int CHUNK_SIZE = 10_000;
public static void main(String[] args) {
for (int i = 0; i < 100; ++i) {
long startTime = System.nanoTime();
for (int j = 0; j < CHUNK_SIZE; ++j) {
new Object();
}
long endTime = System.nanoTime();
System.out.printf("%d\t%d%n", i, endTime - startTime);
}
}
执行结果如下:
0 351833
1 305291
2 305375
3 305708
4 310625
5 308250
6 319709
7 108333
8 38667
9 37791
10 38791
11 38916
12 37791
...
31 38708
32 40333
33 63834
34 20958
35 3583
36 3250
37 3209
38 3125
39 3250
40 3125
...
96 3167
97 3125
98 3125
99 3167
使用Python分析执行结果的代码如下:
import matplotlib.pyplot as plt
# 数据
indices = list(range(100))
values = [
351833, 305291, 305375, 305708, 310625, 308250, 319709, 108333, 38667, 37791,
38791, 38916, 37791, 68500, 84833, 54792, 39167, 44416, 126375, 83584,
55291, 41625, 46584, 37792, 37667, 38750, 38375, 38542, 47042, 39292,
39417, 38708, 40333, 63834, 20958, 3583, 3250, 3209, 3125, 3250,
3125, 3166, 3167, 3166, 3125, 3125, 3209, 3209, 3166, 3208,
3125, 3167, 3125, 3125, 3167, 3209, 3125, 3250, 3167, 3167,
3166, 3125, 3166, 3125, 3083, 3125, 3125, 3167, 3166, 3166,
3208, 3167, 3125, 3167, 3208, 3166, 3125, 3125, 3250, 3208,
3125, 3166, 3125, 3709, 3125, 3125, 3125, 3125, 3167, 3125,
3166, 3167, 3125, 3208, 3167, 3084, 3167, 3125, 3125, 3167
]
# 绘制折线图
plt.figure(figsize=(12, 6))
plt.plot(indices, values, marker='o', linestyle='-', color='b')
plt.title('Trend Analysis')
plt.xlabel('Index')
plt.ylabel('Value')
plt.grid(True)
# 显示图表
plt.show()
结果如下:
能看出来什么呢?相同的代码随着程序不断执行,执行耗时共经历了三个平台期,并在第三个平台期趋于稳定。
JIT编译快速上手
JIT编译是JVM中的一种优化技术。简单说就是JVM会针对热点代码进行优化,经过JIT编译的代码执行效率会更高。热点代码是执行次数超过阈值的代码,该阈值可以通过JVM参数设置
- 优点:提高执行代码执行效率
- 缺点:应用刚启动时,JVM会占用CPU对热点代码进行优化,导致执行业务代码的CPU核心数减少,通常表现为应用启动时机器load增高。
阅读
读后应该了解的:
- JVM中编译器的种类
- 分层编译
- 编译优化的方式有哪些
- CodeCache是什么
JIT相关JVM参数
由于编译情况复杂,JVM也会动态调整相关的阈值来保证JVM的性能,所以不建议手动调整编译相关的参数。
常规参数
| 参数 | 解释 |
|---|---|
| -XX:+TieredCompilation | 开启分层编译,JDK8之后默认开启 |
| -XX:+CICompilerCount=N | 编译线程数,设置数量后,JVM会自动分配线程数,C1:C2 = 1:2 。以4核CPU为例,N=3时C1编译器会被分配1个CPU核心,C2编译器会被分配2个CPU核心数 |
| -XX:ReservedCodeCacheSize | CodeCache的最大大小 |
| -XX:InitialCodeCacheSize | CodeCache的初始大小 |
编译阈值
关于编译阈值,网上很多资料都说由参数-XX:CompileThreshold确定,而在开启分层编译后该参数就会失效,并使用另外三个参数,通过更复杂的方式计算得出变异阈值。
而在分层编译的情况下,-XX: CompileThreshold 指定的阈值将失效,此时将会根据当前待编译的方法数以及编译线程数来动态调整
分层编译触发条件公式
i > TierXInvocationThreshold * s || (i > TierXMinInvocationThreshold * s >&& i + b > TierXCompileThreshold * s) i为调用次数,b是循环回边次数
公式中的s是JVM通过待编译方法数和编译线程数动态调整的,所以我们很难通过手动调整JVM参数确定具体的编译阈值,只能通过不断调大or调小参数,以期找到合适的参数。