两种可行高性能惰性单例的实现
已知 invokedynamic是线程安全的,MethodHandles.constant支持MH返回一个常量,那么结合起来如何呢?
在java.lang.Class
中存在一个特殊的字段classData
可以用于存放和这个类相关联的一个对象,当我们定一个hidden class时使用lookup.defineHiddenClassWithClassData(classByteCode, factory, false)
即可对其进行赋值,当方法执行到对应的invokedynamic时我们使用MethodHandles.classData(lookup, DEFAULT_NAME, Supplier.class)
再将其取出来,包裹下变成ConstantCallSite
返回即可
ConstantDynamic是一个特殊的字节码,其储存在常量池中,当被load到栈顶时会执行BootstrapMethod,然后被jit将返回值缓存起来,之后再次调用时直接返回缓存的值,这个特性正好可以用于实现高性能的懒加载单例。
具体可以参考JEP 309: Dynamic Class-File Constants 以及 hands-on-java-constantdynamic
请参考issue的讨论
可以执行下面的命令进行benchmark
sh ./benchmark.sh
在我本人的机子上可以跑出来如下结果
并发线程数目为5
stableValue.Benchmark.StableValueBenchmarkCase.testClassInit thrpt 10 2900813.932 ± 26353.332 ops/s
stableValue.Benchmark.StableValueBenchmarkCase.testDCL thrpt 10 210772.581 ± 2527.113 ops/s
stableValue.Benchmark.StableValueBenchmarkCase.testIndyStabValue thrpt 10 2902634.437 ± 25928.781 ops/s
stableValue.Benchmark.StableValueBenchmarkCase.testIndyStabValueCody thrpt 10 2902663.558 ± 23987.111 ops/s
stableValue.Benchmark.StableValueBenchmarkCase.testIndyStabValueHidden thrpt 10 2900383.951 ± 24068.082 ops/s
stableValue.Benchmark.StableValueBenchmarkCase.testJdkStableValue thrpt 10 2892260.871 ± 45261.131 ops/s
stableValue.Benchmark.StableValueBenchmarkCase.testPlain thrpt 10 2918079.807 ± 4314.742 ops/s