为什么对UUID.randomUUID()的初始调用会变慢?

时间:2017-01-23 00:38:33

标签: java performance uuid

鉴于以下代码片段生成UUID.randomUUID(),我得到以下性能结果(以毫秒为单位):

public static void main(String[] args) {
    long tmp = System.currentTimeMillis();
    UUID.randomUUID();
    tmp = printDiff(tmp);
    UUID.randomUUID();
    tmp = printDiff(tmp);
    UUID.randomUUID();
    tmp = printDiff(tmp);
    UUID.randomUUID();
    tmp = printDiff(tmp);
}

private static long printDiff(final long previousTimestamp) {
    long tmp = System.currentTimeMillis();
    System.out.printf("%s%n", tmp - previousTimestamp);
    return tmp;
}

结果:

971
6
0
0

JDK: 1.8 操作系统:Windows 7

为什么只有初始通话需要这么长时间? (将近1秒!)

3 个答案:

答案 0 :(得分:7)

一次完成SecureRandom的初始化:

//from the source code of randomUUID
private static class Holder {
    static final SecureRandom numberGenerator = new SecureRandom();
}

但这并不是全部。那些零应该真的跳到你的脸上。因此操作耗时0毫秒;这是否意味着他们减少了?像几纳秒或者你做错了什么?

有一个适当的工具来衡量这些东西,称为jmh。

@BenchmarkMode({ Mode.AverageTime, Mode.SingleShotTime })
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class UUIDRandom {

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(UUIDRandom.class.getSimpleName()).build();
        new Runner(opt).run();
    }

    @Benchmark
    @Fork(1)
    public UUID random() {
        return UUID.randomUUID();
    }
}

输出说:

Benchmark          Mode  Cnt  Score   Error  Units
UUIDRandom.random  avgt    2  0.002          ms/op
UUIDRandom.random    ss    2  0.094          ms/op

确实单发时间远远低于平均时间。

答案 1 :(得分:0)

第一次调用UUID.randomUUID()时,它必须初始化它在所有后续调用中使用的一些内部对象。

source code for UUID.randomUUID是:

public static UUID randomUUID() {
    SecureRandom ng = Holder.numberGenerator;

    byte[] randomBytes = new byte[16];
    ng.nextBytes(randomBytes);
    randomBytes[6]  &= 0x0f;  /* clear version        */
    randomBytes[6]  |= 0x40;  /* set to version 4     */
    randomBytes[8]  &= 0x3f;  /* clear variant        */
    randomBytes[8]  |= 0x80;  /* set to IETF variant  */
    return new UUID(randomBytes);
}

这里,Holder.numberGenerator是一个全局变量,在首次使用时,必须进行初始化:

private static class Holder {
    static final SecureRandom numberGenerator = new SecureRandom();
}

答案 2 :(得分:0)

基于Java 8代码,看起来创建SecureRandom对象看起来很昂贵。这就是为什么他们将初始化推迟到需要(也就是延迟初始化)并在后续调用中重用它。

/*
 * The random number generator used by this class to create random
 * based UUIDs. In a holder class to defer initialization until needed.
 */
private static class Holder {
    static final SecureRandom numberGenerator = new SecureRandom();
}