在类初始化期间计算不可变数据结果,并将结果保存在static final字段中是一种非常常见的做法。实际上,这正是静态初始化器的设计目标。
以下是在初始化时构建一些静态表的典型示例:
public class StaticExample {
static final long[] TABLE = new long[100_000_000];
static {
TABLE[0] = 0;
for (int i = 1; i < TABLE.length; i++) {
TABLE[i] = nextValue(TABLE[i - 1]);
}
}
private static long nextValue(long seed) {
return seed * 0x123456789L + 11;
}
...
}
在我的JDK 11.0.1笔记本电脑上,静态初始化程序在大约540毫秒内填充100M元素的数组。
现在让我们简单地删除static并填充构造函数中的数组。
public class NonStaticExample {
final long[] TABLE = new long[100_000_000];
{
TABLE[0] = 0;
for (int i = 1; i < TABLE.length; i++) {
TABLE[i] = nextValue(TABLE[i - 1]);
}
}
private static long nextValue(long seed) {
return seed * 0x123456789L + 11;
}
public static void main(String[] args) {
new NonStaticExample();
}
}
构造函数在138毫秒内填充类似的数组。几乎快4倍!
为什么静态初始化器会变慢?这必须与JIT编译有关。
解决方法非常简单:
只是不要直接在未初始化的类中进行繁重的计算。如果将计算逻辑放在没有静态初始化程序的辅助类中,它将不会受到性能损失的影响。
public class StaticExample {
static final long[] TABLE = Helper.prepareTable();
private static class Helper {
static long[] prepareTable() {
long[] table = new long[100_000_000];
for (int i = 1; i < table.length; i++) {
table[i] = nextValue(table[i - 1]);
}
return table;
}
static long nextValue(long seed) {
return seed * 0x123456789L + 11;
}
}
}
原文地址:https://blog.51cto.com/13754022/2376668
时间: 2024-10-13 12:36:39