2017-01-23 302 views
3

考慮下面的代碼片段,從而產生一個UUID.randomUUID(),我得到下面的性能結果(以毫秒爲單位):爲什麼對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 OS : Windows 7

爲什麼只有最初的通話需要這麼長時間嗎? (接近1秒!)

+0

@ 4castle它不是,只有4調用這裏,沒有JIT – Eugene

+1

的SecureRandom的初始化可以在Windows上很慢,由於網絡接口掃描等,參見[這個問題](HTTP://計算器。 com/questions/38942514/simple-java-program-100-times-after-plug-in-usb-hotspot)瞭解詳情。 – apangin

回答

6

它是一次完成的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 

事實上,單次時間差很遠,則平均水平。

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(); 
} 
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(); 
}