Re: RFR: 8156071: List.of: reduce array copying during creation
On Tue, 6 Oct 2020 05:07:37 GMT, Tagir F. Valeev wrote: >> Sorry to be late to the party. I thought that all reviews labeled with >> core-libs should be mirrored to core-libs-dev >> mailing list but I haven't seen it there :( >> Please note that the integrated implementation exposes listFromTrustedArray >> to everybody. No dirty unsafe reflection is >> necessary, only single unchecked cast: >> static List untrustedArrayToList(T[] array) { >> @SuppressWarnings("unchecked") >> Function, List> finisher = >> (Function, List>) >> Collectors.toUnmodifiableList().finisher(); >> ArrayList list = new ArrayList<>() { >> @Override >> public Object[] toArray() { >> return array; >> } >> }; >> return finisher.apply(list); >> } >> >> This might be qualified as a security issue. > > This could be fixed by adding a classword check to the finisher, like this: > >list -> { > if (list.getClass() != > ArrayList.class) { > throw new > IllegalArgumentException(); > } > return (List) > SharedSecrets.getJavaUtilCollectionAccess() > > .listFromTrustedArray(list.toArray()); >}, Thanks for pointing this out. I've filed bug [JDK-8254090](https://bugs.openjdk.java.net/browse/JDK-8254090). I think we're ok as long as this gets fixed before JDK 16 ships. I think the notification messages for this did end up on core-libs-dev, but perhaps there were some email delays over the weekend. - PR: https://git.openjdk.java.net/jdk/pull/449
Re: RFR: 8156071: List.of: reduce array copying during creation
On Tue, 6 Oct 2020 03:10:34 GMT, Tagir F. Valeev wrote: >> Looks good, i wondered why the performance results were so slow then i >> looked more closely and saw "-Xint" was used. I >> usually don't ascribe much value to micro benchmarks run in interpreter only >> mode, but hey any shaving off startup time >> is welcome. Less allocation is definitely welcome (although i do wish C2 was >> better at eliding redundant array >> initialization and allocation). > > Sorry to be late to the party. I thought that all reviews labeled with > core-libs should be mirrored to core-libs-dev > mailing list but I haven't seen it there :( > Please note that the integrated implementation exposes listFromTrustedArray > to everybody. No dirty unsafe reflection is > necessary, only single unchecked cast: > static List untrustedArrayToList(T[] array) { > @SuppressWarnings("unchecked") > Function, List> finisher = > (Function, List>) > Collectors.toUnmodifiableList().finisher(); > ArrayList list = new ArrayList<>() { > @Override > public Object[] toArray() { > return array; > } > }; > return finisher.apply(list); > } > > This might be qualified as a security issue. This could be fixed by adding a classword check to the finisher, like this: list -> { if (list.getClass() != ArrayList.class) { throw new IllegalArgumentException(); } return (List) SharedSecrets.getJavaUtilCollectionAccess() .listFromTrustedArray(list.toArray()); }, - PR: https://git.openjdk.java.net/jdk/pull/449
Re: RFR: 8156071: List.of: reduce array copying during creation
On Fri, 2 Oct 2020 20:38:40 GMT, Paul Sandoz wrote: >> Plumb new internal static factory method to trust the array passed in, >> avoiding unnecessary copying. JMH results for >> the benchmark show about 15% improvement for the cases that were optimized, >> namely the 3 to 10 fixed arg cases. >> # VM options: -verbose:gc -XX:+UseParallelGC -Xms4g -Xmx4g --enable-preview >> -verbose:gc -XX:+UsePara >> llelGC -Xms4g -Xmx4g -Xint >> # Warmup: 5 iterations, 1 s each >> # Measurement: 5 iterations, 2 s each >> >> WITHOUT varargs optimization: >> >> Benchmark Mode Cnt Score Error Units >> ListArgs.list00 thrpt 15 6019.539 ± 144.040 ops/ms >> ListArgs.list01 thrpt 15 1985.009 ± 40.606 ops/ms >> ListArgs.list02 thrpt 15 1854.812 ± 17.488 ops/ms >> ListArgs.list03 thrpt 15 963.866 ± 10.262 ops/ms >> ListArgs.list04 thrpt 15 908.116 ± 6.278 ops/ms >> ListArgs.list05 thrpt 15 848.607 ± 16.701 ops/ms >> ListArgs.list06 thrpt 15 822.282 ± 8.905 ops/ms >> ListArgs.list07 thrpt 15 780.057 ± 11.214 ops/ms >> ListArgs.list08 thrpt 15 745.295 ± 19.204 ops/ms >> ListArgs.list09 thrpt 15 704.596 ± 14.003 ops/ms >> ListArgs.list10 thrpt 15 696.436 ± 4.914 ops/ms >> ListArgs.list11 thrpt 15 661.908 ± 11.041 ops/ms >> >> WITH varargs optimization: >> >> Benchmark Mode Cnt ScoreError Units >> ListArgs.list00 thrpt 15 6172.298 ± 62.736 ops/ms >> ListArgs.list01 thrpt 15 1987.724 ± 45.468 ops/ms >> ListArgs.list02 thrpt 15 1843.419 ± 10.693 ops/ms >> ListArgs.list03 thrpt 15 1126.946 ± 30.952 ops/ms >> ListArgs.list04 thrpt 15 1050.440 ± 17.859 ops/ms >> ListArgs.list05 thrpt 15 999.275 ± 23.656 ops/ms >> ListArgs.list06 thrpt 15 948.844 ± 19.615 ops/ms >> ListArgs.list07 thrpt 15 897.541 ± 15.531 ops/ms >> ListArgs.list08 thrpt 15 853.359 ± 18.755 ops/ms >> ListArgs.list09 thrpt 15 826.394 ± 8.284 ops/ms >> ListArgs.list10 thrpt 15 779.231 ± 4.104 ops/ms >> ListArgs.list11 thrpt 15 650.888 ± 3.948 ops/ms > > Looks good, i wondered why the performance results were so slow then i looked > more closely and saw "-Xint" was used. I > usually don't ascribe much value to micro benchmarks run in interpreter only > mode, but hey any shaving off startup time > is welcome. Less allocation is definitely welcome (although i do wish C2 was > better at eliding redundant array > initialization and allocation). Sorry to be late to the party. I thought that all reviews labeled with core-libs should be mirrored to core-libs-dev mailing list but I haven't seen it there :( Please note that the integrated implementation exposes listFromTrustedArray to everybody. No dirty unsafe reflection is necessary, only single unchecked cast: static List untrustedArrayToList(T[] array) { @SuppressWarnings("unchecked") Function, List> finisher = (Function, List>) Collectors.toUnmodifiableList().finisher(); ArrayList list = new ArrayList<>() { @Override public Object[] toArray() { return array; } }; return finisher.apply(list); } This might be qualified as a security issue. - PR: https://git.openjdk.java.net/jdk/pull/449
Re: RFR: 8156071: List.of: reduce array copying during creation
On Thu, 1 Oct 2020 00:13:28 GMT, Stuart Marks wrote: > Plumb new internal static factory method to trust the array passed in, > avoiding unnecessary copying. JMH results for > the benchmark show about 15% improvement for the cases that were optimized, > namely the 3 to 10 fixed arg cases. > # VM options: -verbose:gc -XX:+UseParallelGC -Xms4g -Xmx4g --enable-preview > -verbose:gc -XX:+UsePara > llelGC -Xms4g -Xmx4g -Xint > # Warmup: 5 iterations, 1 s each > # Measurement: 5 iterations, 2 s each > > WITHOUT varargs optimization: > > Benchmark Mode Cnt Score Error Units > ListArgs.list00 thrpt 15 6019.539 ± 144.040 ops/ms > ListArgs.list01 thrpt 15 1985.009 ± 40.606 ops/ms > ListArgs.list02 thrpt 15 1854.812 ± 17.488 ops/ms > ListArgs.list03 thrpt 15 963.866 ± 10.262 ops/ms > ListArgs.list04 thrpt 15 908.116 ± 6.278 ops/ms > ListArgs.list05 thrpt 15 848.607 ± 16.701 ops/ms > ListArgs.list06 thrpt 15 822.282 ± 8.905 ops/ms > ListArgs.list07 thrpt 15 780.057 ± 11.214 ops/ms > ListArgs.list08 thrpt 15 745.295 ± 19.204 ops/ms > ListArgs.list09 thrpt 15 704.596 ± 14.003 ops/ms > ListArgs.list10 thrpt 15 696.436 ± 4.914 ops/ms > ListArgs.list11 thrpt 15 661.908 ± 11.041 ops/ms > > WITH varargs optimization: > > Benchmark Mode Cnt ScoreError Units > ListArgs.list00 thrpt 15 6172.298 ± 62.736 ops/ms > ListArgs.list01 thrpt 15 1987.724 ± 45.468 ops/ms > ListArgs.list02 thrpt 15 1843.419 ± 10.693 ops/ms > ListArgs.list03 thrpt 15 1126.946 ± 30.952 ops/ms > ListArgs.list04 thrpt 15 1050.440 ± 17.859 ops/ms > ListArgs.list05 thrpt 15 999.275 ± 23.656 ops/ms > ListArgs.list06 thrpt 15 948.844 ± 19.615 ops/ms > ListArgs.list07 thrpt 15 897.541 ± 15.531 ops/ms > ListArgs.list08 thrpt 15 853.359 ± 18.755 ops/ms > ListArgs.list09 thrpt 15 826.394 ± 8.284 ops/ms > ListArgs.list10 thrpt 15 779.231 ± 4.104 ops/ms > ListArgs.list11 thrpt 15 650.888 ± 3.948 ops/ms Looks good, i wondered why the performance results were so slow then i looked more closely and saw "-Xint" was used. I usually don't ascribe much value to micro benchmarks run in interpreter only mode, but hey any shaving off startup time is welcome. Less allocation is definitely welcome (although i do wish C2 was better at eliding redundant array initialization and allocation). - Marked as reviewed by psandoz (Reviewer). PR: https://git.openjdk.java.net/jdk/pull/449
Re: RFR: 8156071: List.of: reduce array copying during creation
On Thu, 1 Oct 2020 06:26:39 GMT, Martin Grigorov wrote: >> Plumb new internal static factory method to trust the array passed in, >> avoiding unnecessary copying. JMH results for >> the benchmark show about 15% improvement for the cases that were optimized, >> namely the 3 to 10 fixed arg cases. >> # VM options: -verbose:gc -XX:+UseParallelGC -Xms4g -Xmx4g --enable-preview >> -verbose:gc -XX:+UsePara >> llelGC -Xms4g -Xmx4g -Xint >> # Warmup: 5 iterations, 1 s each >> # Measurement: 5 iterations, 2 s each >> >> WITHOUT varargs optimization: >> >> Benchmark Mode Cnt Score Error Units >> ListArgs.list00 thrpt 15 6019.539 ± 144.040 ops/ms >> ListArgs.list01 thrpt 15 1985.009 ± 40.606 ops/ms >> ListArgs.list02 thrpt 15 1854.812 ± 17.488 ops/ms >> ListArgs.list03 thrpt 15 963.866 ± 10.262 ops/ms >> ListArgs.list04 thrpt 15 908.116 ± 6.278 ops/ms >> ListArgs.list05 thrpt 15 848.607 ± 16.701 ops/ms >> ListArgs.list06 thrpt 15 822.282 ± 8.905 ops/ms >> ListArgs.list07 thrpt 15 780.057 ± 11.214 ops/ms >> ListArgs.list08 thrpt 15 745.295 ± 19.204 ops/ms >> ListArgs.list09 thrpt 15 704.596 ± 14.003 ops/ms >> ListArgs.list10 thrpt 15 696.436 ± 4.914 ops/ms >> ListArgs.list11 thrpt 15 661.908 ± 11.041 ops/ms >> >> WITH varargs optimization: >> >> Benchmark Mode Cnt ScoreError Units >> ListArgs.list00 thrpt 15 6172.298 ± 62.736 ops/ms >> ListArgs.list01 thrpt 15 1987.724 ± 45.468 ops/ms >> ListArgs.list02 thrpt 15 1843.419 ± 10.693 ops/ms >> ListArgs.list03 thrpt 15 1126.946 ± 30.952 ops/ms >> ListArgs.list04 thrpt 15 1050.440 ± 17.859 ops/ms >> ListArgs.list05 thrpt 15 999.275 ± 23.656 ops/ms >> ListArgs.list06 thrpt 15 948.844 ± 19.615 ops/ms >> ListArgs.list07 thrpt 15 897.541 ± 15.531 ops/ms >> ListArgs.list08 thrpt 15 853.359 ± 18.755 ops/ms >> ListArgs.list09 thrpt 15 826.394 ± 8.284 ops/ms >> ListArgs.list10 thrpt 15 779.231 ± 4.104 ops/ms >> ListArgs.list11 thrpt 15 650.888 ± 3.948 ops/ms > > src/java.base/share/classes/jdk/internal/access/SharedSecrets.java line 88: > >> 86: if (javaUtilCollectionAccess == null) { >> 87: try { >> 88: Class.forName("java.util.ImmutableCollections$Access", >> true, null); > > How does this work ? It attempts to load this class but > `javaUtilCollectionAccess` is never assigned to a new value. > > **Update**: I just noticed this is the getter. Yeah the SharedSecrets stuff is pretty twisted - PR: https://git.openjdk.java.net/jdk/pull/449
Re: RFR: 8156071: List.of: reduce array copying during creation
On Thu, 1 Oct 2020 00:13:28 GMT, Stuart Marks wrote: > Plumb new internal static factory method to trust the array passed in, > avoiding unnecessary copying. JMH results for > the benchmark show about 15% improvement for the cases that were optimized, > namely the 3 to 10 fixed arg cases. > # VM options: -verbose:gc -XX:+UseParallelGC -Xms4g -Xmx4g --enable-preview > -verbose:gc -XX:+UsePara > llelGC -Xms4g -Xmx4g -Xint > # Warmup: 5 iterations, 1 s each > # Measurement: 5 iterations, 2 s each > > WITHOUT varargs optimization: > > Benchmark Mode Cnt Score Error Units > ListArgs.list00 thrpt 15 6019.539 ± 144.040 ops/ms > ListArgs.list01 thrpt 15 1985.009 ± 40.606 ops/ms > ListArgs.list02 thrpt 15 1854.812 ± 17.488 ops/ms > ListArgs.list03 thrpt 15 963.866 ± 10.262 ops/ms > ListArgs.list04 thrpt 15 908.116 ± 6.278 ops/ms > ListArgs.list05 thrpt 15 848.607 ± 16.701 ops/ms > ListArgs.list06 thrpt 15 822.282 ± 8.905 ops/ms > ListArgs.list07 thrpt 15 780.057 ± 11.214 ops/ms > ListArgs.list08 thrpt 15 745.295 ± 19.204 ops/ms > ListArgs.list09 thrpt 15 704.596 ± 14.003 ops/ms > ListArgs.list10 thrpt 15 696.436 ± 4.914 ops/ms > ListArgs.list11 thrpt 15 661.908 ± 11.041 ops/ms > > WITH varargs optimization: > > Benchmark Mode Cnt ScoreError Units > ListArgs.list00 thrpt 15 6172.298 ± 62.736 ops/ms > ListArgs.list01 thrpt 15 1987.724 ± 45.468 ops/ms > ListArgs.list02 thrpt 15 1843.419 ± 10.693 ops/ms > ListArgs.list03 thrpt 15 1126.946 ± 30.952 ops/ms > ListArgs.list04 thrpt 15 1050.440 ± 17.859 ops/ms > ListArgs.list05 thrpt 15 999.275 ± 23.656 ops/ms > ListArgs.list06 thrpt 15 948.844 ± 19.615 ops/ms > ListArgs.list07 thrpt 15 897.541 ± 15.531 ops/ms > ListArgs.list08 thrpt 15 853.359 ± 18.755 ops/ms > ListArgs.list09 thrpt 15 826.394 ± 8.284 ops/ms > ListArgs.list10 thrpt 15 779.231 ± 4.104 ops/ms > ListArgs.list11 thrpt 15 650.888 ± 3.948 ops/ms After a hint from @cl4es I ran the benchmarks with `-prof gc`. The allocation rate is reduced by about 40% per operation in the cases where the optimization was applied. WITHOUT varargs optimization: ListArgs.list00:·gc.alloc.rate.norm thrpt5≈ 10⁻⁴ B/op ListArgs.list01:·gc.alloc.rate.norm thrpt524.000 ±0.001 B/op ListArgs.list02:·gc.alloc.rate.norm thrpt524.000 ±0.001 B/op ListArgs.list03:·gc.alloc.rate.norm thrpt580.000 ±0.001 B/op ListArgs.list04:·gc.alloc.rate.norm thrpt580.036 ±0.309 B/op ListArgs.list05:·gc.alloc.rate.norm thrpt596.037 ±0.316 B/op ListArgs.list06:·gc.alloc.rate.norm thrpt596.038 ±0.326 B/op ListArgs.list07:·gc.alloc.rate.norm thrpt5 112.042 ±0.361 B/op ListArgs.list08:·gc.alloc.rate.norm thrpt5 112.043 ±0.367 B/op ListArgs.list09:·gc.alloc.rate.norm thrpt5 128.045 ±0.385 B/op ListArgs.list10:·gc.alloc.rate.norm thrpt5 128.046 ±0.391 B/op ListArgs.list11:·gc.alloc.rate.norm thrpt5 144.047 ±0.406 B/op WITH varargs optimization: ListArgs.list00:·gc.alloc.rate.norm thrpt5≈ 10⁻⁴ B/op ListArgs.list01:·gc.alloc.rate.norm thrpt524.000 ±0.001 B/op ListArgs.list02:·gc.alloc.rate.norm thrpt524.000 ±0.001 B/op ListArgs.list03:·gc.alloc.rate.norm thrpt548.000 ±0.001 B/op ListArgs.list04:·gc.alloc.rate.norm thrpt548.000 ±0.001 B/op ListArgs.list05:·gc.alloc.rate.norm thrpt556.000 ±0.001 B/op ListArgs.list06:·gc.alloc.rate.norm thrpt556.000 ±0.001 B/op ListArgs.list07:·gc.alloc.rate.norm thrpt564.000 ±0.001 B/op ListArgs.list08:·gc.alloc.rate.norm thrpt564.000 ±0.001 B/op ListArgs.list09:·gc.alloc.rate.norm thrpt572.000 ±0.001 B/op ListArgs.list10:·gc.alloc.rate.norm thrpt572.000 ±0.001 B/op ListArgs.list11:·gc.alloc.rate.norm thrpt5 144.050 ±0.427 B/op - PR: https://git.openjdk.java.net/jdk/pull/449
Re: RFR: 8156071: List.of: reduce array copying during creation
On Thu, 1 Oct 2020 00:13:28 GMT, Stuart Marks wrote: > Plumb new internal static factory method to trust the array passed in, > avoiding unnecessary copying. JMH results for > the benchmark show about 15% improvement for the cases that were optimized, > namely the 3 to 10 fixed arg cases. > # VM options: -verbose:gc -XX:+UseParallelGC -Xms4g -Xmx4g --enable-preview > -verbose:gc -XX:+UsePara > llelGC -Xms4g -Xmx4g -Xint > # Warmup: 5 iterations, 1 s each > # Measurement: 5 iterations, 2 s each > > WITHOUT varargs optimization: > > Benchmark Mode Cnt Score Error Units > ListArgs.list00 thrpt 15 6019.539 ± 144.040 ops/ms > ListArgs.list01 thrpt 15 1985.009 ± 40.606 ops/ms > ListArgs.list02 thrpt 15 1854.812 ± 17.488 ops/ms > ListArgs.list03 thrpt 15 963.866 ± 10.262 ops/ms > ListArgs.list04 thrpt 15 908.116 ± 6.278 ops/ms > ListArgs.list05 thrpt 15 848.607 ± 16.701 ops/ms > ListArgs.list06 thrpt 15 822.282 ± 8.905 ops/ms > ListArgs.list07 thrpt 15 780.057 ± 11.214 ops/ms > ListArgs.list08 thrpt 15 745.295 ± 19.204 ops/ms > ListArgs.list09 thrpt 15 704.596 ± 14.003 ops/ms > ListArgs.list10 thrpt 15 696.436 ± 4.914 ops/ms > ListArgs.list11 thrpt 15 661.908 ± 11.041 ops/ms > > WITH varargs optimization: > > Benchmark Mode Cnt ScoreError Units > ListArgs.list00 thrpt 15 6172.298 ± 62.736 ops/ms > ListArgs.list01 thrpt 15 1987.724 ± 45.468 ops/ms > ListArgs.list02 thrpt 15 1843.419 ± 10.693 ops/ms > ListArgs.list03 thrpt 15 1126.946 ± 30.952 ops/ms > ListArgs.list04 thrpt 15 1050.440 ± 17.859 ops/ms > ListArgs.list05 thrpt 15 999.275 ± 23.656 ops/ms > ListArgs.list06 thrpt 15 948.844 ± 19.615 ops/ms > ListArgs.list07 thrpt 15 897.541 ± 15.531 ops/ms > ListArgs.list08 thrpt 15 853.359 ± 18.755 ops/ms > ListArgs.list09 thrpt 15 826.394 ± 8.284 ops/ms > ListArgs.list10 thrpt 15 779.231 ± 4.104 ops/ms > ListArgs.list11 thrpt 15 650.888 ± 3.948 ops/ms src/java.base/share/classes/jdk/internal/access/SharedSecrets.java line 88: > 86: if (javaUtilCollectionAccess == null) { > 87: try { > 88: Class.forName("java.util.ImmutableCollections$Access", > true, null); How does this work ? It attempts to load this class but `javaUtilCollectionAccess` is never assigned to a new value. **Update**: I just noticed this is the getter. - PR: https://git.openjdk.java.net/jdk/pull/449
RFR: 8156071: List.of: reduce array copying during creation
Plumb new internal static factory method to trust the array passed in, avoiding unnecessary copying. JMH results for the benchmark show about 15% improvement for the cases that were optimized, namely the 3 to 10 fixed arg cases. # VM options: -verbose:gc -XX:+UseParallelGC -Xms4g -Xmx4g --enable-preview -verbose:gc -XX:+UsePara llelGC -Xms4g -Xmx4g -Xint # Warmup: 5 iterations, 1 s each # Measurement: 5 iterations, 2 s each WITHOUT varargs optimization: Benchmark Mode Cnt Score Error Units ListArgs.list00 thrpt 15 6019.539 ± 144.040 ops/ms ListArgs.list01 thrpt 15 1985.009 ± 40.606 ops/ms ListArgs.list02 thrpt 15 1854.812 ± 17.488 ops/ms ListArgs.list03 thrpt 15 963.866 ± 10.262 ops/ms ListArgs.list04 thrpt 15 908.116 ± 6.278 ops/ms ListArgs.list05 thrpt 15 848.607 ± 16.701 ops/ms ListArgs.list06 thrpt 15 822.282 ± 8.905 ops/ms ListArgs.list07 thrpt 15 780.057 ± 11.214 ops/ms ListArgs.list08 thrpt 15 745.295 ± 19.204 ops/ms ListArgs.list09 thrpt 15 704.596 ± 14.003 ops/ms ListArgs.list10 thrpt 15 696.436 ± 4.914 ops/ms ListArgs.list11 thrpt 15 661.908 ± 11.041 ops/ms WITH varargs optimization: Benchmark Mode Cnt ScoreError Units ListArgs.list00 thrpt 15 6172.298 ± 62.736 ops/ms ListArgs.list01 thrpt 15 1987.724 ± 45.468 ops/ms ListArgs.list02 thrpt 15 1843.419 ± 10.693 ops/ms ListArgs.list03 thrpt 15 1126.946 ± 30.952 ops/ms ListArgs.list04 thrpt 15 1050.440 ± 17.859 ops/ms ListArgs.list05 thrpt 15 999.275 ± 23.656 ops/ms ListArgs.list06 thrpt 15 948.844 ± 19.615 ops/ms ListArgs.list07 thrpt 15 897.541 ± 15.531 ops/ms ListArgs.list08 thrpt 15 853.359 ± 18.755 ops/ms ListArgs.list09 thrpt 15 826.394 ± 8.284 ops/ms ListArgs.list10 thrpt 15 779.231 ± 4.104 ops/ms ListArgs.list11 thrpt 15 650.888 ± 3.948 ops/ms - Commit messages: - 8156071: reduce varargs array creation and unnecessary copying during creation Changes: https://git.openjdk.java.net/jdk/pull/449/files Webrev: https://webrevs.openjdk.java.net/?repo=jdk&pr=449&range=00 Issue: https://bugs.openjdk.java.net/browse/JDK-8156071 Stats: 203 lines in 6 files changed: 185 ins; 0 del; 18 mod Patch: https://git.openjdk.java.net/jdk/pull/449.diff Fetch: git fetch https://git.openjdk.java.net/jdk pull/449/head:pull/449 PR: https://git.openjdk.java.net/jdk/pull/449