RFR: 8290079: Reduce interaction with volatile in static initializer of BigInteger

Сергей Цыпанов duke at openjdk.org
Tue Jul 12 21:31:46 UTC 2022


On Tue, 12 Jul 2022 09:18:19 GMT, Raffaello Giulietti <duke at openjdk.org> wrote:

>> `BigInteger.powerCache` is volatile and should be assigned only once in static initializer.
>
> Usually yes, but since a static initializer is executed by at most one thread by using a locking protocol before any other static code is ever executed, the runtime _could_ (but I'm not sure it it really does) treat the volatile in the for loop as a local.
> But I would approve your change because it makes this more explicit.

@rgiulietti I've copy-pasted class-loading benchmark from JMH samples

@State(Scope.Thread)
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 5)
@Fork(value = 5, jvmArgsAppend = {"-Xms1g", "-Xmx1g"})
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class Classy {

    @Benchmark
    public Class<?> load() throws ClassNotFoundException {
        return Class.forName("com.tsypanov.slp.Sample", true, new XLoader());
    }

    public static class XLoader extends URLClassLoader {

        private static final byte[] X_BYTECODE = new byte[]{ /*..*/};

        public XLoader() {
            super(new URL[0], ClassLoader.getSystemClassLoader());
        }

        @Override
        protected Class<?> findClass(final String name) {
            return defineClass(name, X_BYTECODE, 0, X_BYTECODE.length);
        }
    }
}

and used it to measure loading a class from `byte[]` copied from the following class compiled:

class Sample {
  static volatile int[] items = new int[100];
  static {
    for (int i = 0; i < items.length; i++) {
      items[i] = ThreadLocalRandom.current().nextInt();
    }
  }
}

I ran the benchmark with `java -jar target/sleep-benchmarks.jar Classy -prof cl` and for the version above got these results (Java 17):

Benchmark                       Mode  Cnt       Score      Error        Units
Classy.load                     avgt   50   96247.202 ±  548.137        ns/op
Classy.load:·class.load         avgt   50  259912.089 ± 1485.314  classes/sec
Classy.load:·class.load.norm    avgt   50       1.000 ±    0.001   classes/op
Classy.load:·class.unload       avgt   50  260243.318 ± 3673.515  classes/sec
Classy.load:·class.unload.norm  avgt   50       1.001 ±    0.014   classes/op

Then I've modified the class in the same way I did in this PR:

class Sample {
  static volatile int[] items;
  static {
    int[] items = new int[100];
    for (int i = 0; i < items.length; i++) {
      items[i] = ThreadLocalRandom.current().nextInt();
    }
    Sample.items = items;
  }
}

and for modified code got


Benchmark                       Mode  Cnt       Score      Error        Units
Classy.load                     avgt   50   63955.673 ±  147.470        ns/op
Classy.load:·class.load         avgt   50  391101.854 ±  925.013  classes/sec
Classy.load:·class.load.norm    avgt   50       1.000 ±    0.001   classes/op
Classy.load:·class.unload       avgt   50  390800.851 ± 2307.589  classes/sec
Classy.load:·class.unload.norm  avgt   50       0.999 ±    0.006   classes/op

>From this I conclude that volatile costs are still there no matter whether we deal with static or non-static initializers.

-------------

PR: https://git.openjdk.org/jdk/pull/9451


More information about the core-libs-dev mailing list