Quando uma nova versão do Java é lançada, os principais recursos são sempre discutidos ativamente. Mas também há trabalho que geralmente permanece "invisível": pequenas otimizações na biblioteca padrão. Eles nos ajudam a acelerar furtivamente nosso código e não exigir nada em troca, e nem mesmo sabemos nada sobre eles!
Esta situação é corrigida por Tagir Valeev (corda), falando sobre essas otimizações. Primeiro, ele falou no Joker 2019 com uma palestra "Java 9-14: pequenas otimizações", você pode assistir ao vídeo dele . Então, como o público gostou muito, no JPoint 2020 ele desenvolveu o tema . E agora decidimos fazer um post para o Habr a partir do segundo relatório para que não só pudesse ser visto, mas também lido.
Além disso, sob o corte, o texto irá em nome do palestrante.
Introdução
Vamos olhar apenas para as coisas mais básicas que todos usam direta ou indiretamente: strings, coleções e reflexão. Não cobrimos APIs desde o Java 8. Todas as melhorias de desempenho são gratuitas quando você executa o código Java 8 em uma JVM mais recente.
. , , , . Intel Core i7-6820HQ Windows 10. , . , +UseParallelGC. , « , ».
String.hashCode
String.hashCode. , - « »:
@Benchmark
public int calcHashCode() {
return " ".hashCode();
}
Java 12 Java 13 :
- 4 ? , . , . : « » « »:
@Benchmark
public int calcHashCode() {
return " ".hashCode();
}
@Benchmark
public int calcHashCode2() {
return " ".hashCode();
}
, Java 12, «» , «» . Java 13 . ?
-. Java 9 Java 12 hashCode() ( -, Compact Strings):
/** Cache the hash code for the string */
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
hash = h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
}
return h;
}
- , . 4 - 0. - . , - , 0 . — , , .
JPoint 2015 « java.lang.String». Java 8 « » - 0, « » . , , String .
, .
layout String Java 8. , 64- JVM String 4 , , . 1 .
, 32- JVM . , - , .
Java 9 , , . , , Latin-1, UTF-16 . 3 , VM, 3 0. , ?
Java 13, String.hashIsZero:
. - :
private int hash; // Default to 0
private boolean hashIsZero; // Default to false;
public int hashCode() {
int h = hash;
if (h == 0 && !hashIsZero) {
h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
if (h == 0) {
hashIsZero = true;
} else {
hash = h;
}
}
return h;
}
hashIsZero? , hashIsCalculated true, .
, hashCode() , . , , -, 0 ( ). , . hashIsCalculated , hashIsCalculated true , 0, .
, String: , , .
, , , . .
String.concat
+, String.concat(). , . :
@Param({"", "is a very very very very very very very very cool conference!"})
String data;
@Benchmark
public String concat() {
return "JPoint ".concat(data);
}
@Benchmark
public String plus() {
return "JPoint " + data;
}
:
, String.concat() Java 8-14 ~3 , + 15 , ~19 . ?
Java. . , breaking change. String.concat() , : , - 0, . , : , .
, .
, . Java 8 String.concat() , +, Java 9 . Java 12 , Java 13 Java 14 String.concat() + 10%.
String.concat(). Java 9 JEP 280 — invokedynamic-. + , String.concat() JEP 280 .
Java 9 :
, Java . , , String.concat(). ?
String.concat() Java 12:
public String concat(String str) {
if (str.isEmpty()) {
return this;
}
if (coder() == str.coder()) {
byte[] val = this.value;
byte[] oval = str.value;
int len = val.length + oval.length;
byte[] buf = Arrays.copyOf(val, len);
System.arraycopy(oval, 0, buf, val.length, oval.length);
return new String(buf, coder);
}
int len = length();
int olen = str.length();
byte[] buf = StringUTF16.newBytesFor(len + olen);
getBytes(buf, 0, UTF16);
str.getBytes(buf, len, UTF16);
return new String(buf, UTF16);
}
, , . , , .
public String concat(String str) {
if (str.isEmpty()) {
return this;
}
return StringConcatHelper.simpleConcat(this, str);
}
static String simpleConcat(Object first, Object second) {
String s1 = stringOf(first);
String s2 = stringOf(second);
// start "mixing" in length and coder or arguments, order is not
// important
long indexCoder = mix(initialCoder(), s2);
indexCoder = mix(indexCoder, s1);
byte[] buf =(indexCoder);
// prepend each argument in reverse order, since we prepending
// from the end of the byte array
indexCoder = prepend(indexCoder, buf, s2);
indexCoder = prepend(indexCoder, buf, s1);
return newString(buf, indexCoder);
}
Java 13 simpleConcat(), . , , mix() prepend(). newArray():
static byte[] newArray(long indexCoder) {
byte coder = (byte)(indexCoder >> 32);
int index = (int)indexCoder;
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
}
, . . Java 12 newArray() copyOf(), .
.
, , 11 — . , JDK-8247605, Java 16.
simpleConcat():
static String simpleConcat(Object first, Object second) {
String s1 = stringOf(first);
String s2 = stringOf(second);
// start "mixing" in length and coder or arguments, order is not
// important
long indexCoder = mix(initialCoder(), s2);
indexCoder = mix(indexCoder, s1);
byte[] buf =(indexCoder);
// prepend each argument in reverse order, since we prepending
// from the end of the byte array
indexCoder = prepend(indexCoder, buf, s2);
indexCoder = prepend(indexCoder, buf, s1);
return newString(buf, indexCoder);
}
, s1 s2 — . , . . , , . — :
...
String s1 = stringOf(first);
String s2 = stringOf(second);
if (s1.isEmpty()) {
// newly created string required, see JLS 15.18.1
return new String(s2);
}
if (s2.isEmpty()) {
// newly created string required, see JLS 15.18.1
return new String(s1);
}
...
, , , , . , .
Java 16, :
( , ) , , — ~6 .
: , , . , target- Java 9 .
invokedynamic — , .
TreeMap.computeIfAbsent
, Java 8 map:
- putIfAbsent()
- computeIfAbsent()
- computeIfPresent()
- compute()
- merge()
map, , putIfAbsent(), , , . , , . , computeIfAbsent():
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
computeIfAbsent() map, . , null, .
. , : get() - , , put() , . 2 , .
, , map. . Java 8, , map :
TreeMap . — , - . , , .
2017 , Java 10. Code Review, . 2019 , merge(), . Java 15, , :
, . map computeIfAbsent() , . computeIfPresent() , . map , , 10-20 % .
. , , , — .
public BigInteger fibo(int arg) {
if (arg < 1) {
throw new IllegalArgumentException();
}
if (arg <= 2) {
return BigInteger.ONE;
}
return fibo(arg - 1).add(fibo(arg - 2));
}
public static void main(String[] args) {
Fibo fibo = new Fibo();
System.out.println(fibo.fibo(100));
}
100. , . :
Map<Integer, BigInteger> map = new HashMap<>();
private BigInteger calcFibo(int arg) {
if (arg < 1) {
throw new IllegalArgumentException();
}
if (arg <= 2) {
return BigInteger.ONE;
}
return fibo(arg - 1).add(fibo(arg - 2));
}
public BigInteger fibo(int arg) {
BigInteger value = map.get(arg);
if (value == null) {
value = calcFibo(arg);
map.put(arg, value);
}
return value;
}
, , HashMap. calcFibo() , , , .
, :
public static void main(String[] args) {
Fibo fibo = new Fibo();
System.out.println(fibo.fibo(100));
// 354224848179261915075
System.out.println(fibo.map.get(100));
// 354224848179261915075
}
. 100, map. , map:
System.out.println(fibo.map.size());
// 100
100 . , , .
Java 7. . IntelliJ IDEA if fibo() computeIfAbsent(), :
public BigInteger fibo(int arg) {
return map.computeIfAbsent(arg, this::calcFibo);
}
, . null. . map 185. , .
map , . , get() put(), - .
computeIfAbsent() , :
- -.
- , .
- mappingFunction().
- null, null.
- .
- -.
- size 1.
- , 3.
, map, . . - , , . 5 , .
, map, 1. HashMap . , HashMap .
Java 9, , . : ConcurrentModificationException. HashMap .
- computeIfAbsent(), TreeMap HashMap, , , TreeMap . Java 8-14, Java 15 , .
, computeIfAbsent() , . ,
The mapping function should not modify this map during computation.
ArrayList.removeIf
removeIf() Java 8 , :
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
, , Iterator.remove() , , removed, , . Java 8 , .
default- , . ArrayList Java 8, Java 9 . , .
: ArrayList, 0 size-1 :
data = new ArrayList<>();
for (int i = 0; i < size; i++) {
data.add(i);
}
, , . , :
removeAll: list.removeIf(x -> true);
removeHalf: list.removeIf(x -> x % 2 == 0);
removeLast: list.removeIf(x -> x == size - 1);
removeFirst: list.removeIf(x -> x == 0);
removeNone: list.removeIf(x -> false);
, ArrayList.removeIf() Java 8 1000 :
, removeAll removeHalf removeFirst. , ArrayList . , . . , , .
ArrayList subList, , :
. . removeAll 20 , removeHalf — 6 . removeLast removeFirst subList .
«» ? Java 8 subList(0, size).removeIf() , default- . , , .
Java 9:
subList , , Java 9 subList . , . : removeLast removeNone. , subList removeFirst Java 8. .
removeIf() Java 8 :
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces
// left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
, BitSet, , . , BitSet, ArrayList . , ArrayList . . . default-, .
BitSet . , GC .
, removeLast . BitSet :
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
, , , . .
removeAll , newSize 0. , .
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
Java 9? -, , , . subList.
boolean removeIf(Predicate<? super E> filter, int i, final int end) {
Objects.requireNonNull(filter);
int expectedModCount = modCount;
final Object[] es = elementData;
// Optimize for initial run of survivors
for (; i < end && !filter.test(elementAt(es, i)); i++)
;
// Tolerate predicates that reentrantly access the collection for
// read (but writers still get CME), so traverse once to find
// elements to delete, a second pass to physically expunge.
if (i < end) {
…
} else {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return false;
}
}
, i , .
, — , false. , , , .
, - :
if (i < end) {
final int beg = i;
final long[] deathRow = nBits(end - beg); // new long[((n - 1) >> 6) + 1];
deathRow[0] = 1L; // set bit 0
for (i = beg + 1; i < end; i++)
if (filter.test(elementAt(es, i)))
setBit(deathRow, i - beg); // bits[i >> 6] |= 1L << i;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
modCount++;
int w = beg;
for (i = beg; i < end; i++)
if (isClear(deathRow, i - beg)) // (bits[i >> 6] & (1L << i)) == 0;
es[w++] = es[i];
shiftTailOverGap(es, w, end);
return true;
} else { … }
, BitSet « ». nBits(), setBit() isClear long . , , .
BitSet , . removeLast , .
, . , , BitSet , . .
Java 9. Java 9 , . , . .
hashSet.removeIf()
ArrayList, HashSet. - . HashSet : [], [0], [0, 1] . .:
HashSet<List<Integer>> set;
@Setup
public void setup() {
set = IntStream.range(0, 1000)
.mapToObj(i -> IntStream.range(0, i).boxed().collect(Collectors.toList()))
.collect(Collectors.toCollection(HashSet::new));
}
, HashSet . :
@Benchmark
public Set<List<Integer>> removeHalf() {
Set<List<Integer>> copy = new HashSet<>(set);
copy.removeIf(list -> list.size() > 500);
return copy;
}
@Benchmark
public Set<List<Integer>> noRemove() {
return new HashSet<>(set);
}
, . Java 8:
, + , . , , .
Java 9 , 10 % . ? , HashSet.removeIf() ? . Collection.removeIf(), HashSet.iterator().remove(). HashSet.iterator().remove() HashMap.keySet().iterator().remove(). , HashSet HashMap default-, set. HashMap.KeyIterator.remove(), remove(). , HashMap : keySet().iterator(), valueSet().iterator(), entrySet().iterator(), , .
HashIterator. , ? :
removeNode(), , . Java 9 , , .
, . , . - , , .
, , . , , HashMap LinkedHashMap, .
HashMap.containsKey
Java 15 Map. :
HashMap<List<Integer>, String> emptyMap;
HashMap<List<Integer>, String> nonEmptyMap;
List<Integer> key;
@Setup
public void setup() {
emptyMap = new HashMap<>();
nonEmptyMap = new HashMap<>();
nonEmptyMap.put(Collections.emptyList(), "");
key = IntStream.range(0, 500).boxed().collect(Collectors.toList());
}
@Benchmark
public boolean containsInEmpty() {
return emptyMap.containsKey(key);
}
@Benchmark
public boolean containsInNonEmpty() {
return nonEmptyMap.containsKey(key);
}
HashMap. , . -, , 500 . :
Java 14 500 , , Java 15 3 . , -, .
, . HashMap getNode(), - . Java 15 , , .
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
…
}
return null;
}
Java 15 - getNode(). , - , . - , .
final Node<K,V> getNode(Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & (hash = hash(key))]) != null) {
…
}
return null;
}
, containsKey(), get() getOrDefault(). .
computeIfPresent() . null, . .
. , map computeIfPresent().
, computeIfPresent() , getNode(), , , . - computeIfPresent() , null .
Class.getSimpleName()
Reflection. , , . : class.getName(), class.getCanonicalName(), class.getSimpleName(). Class.getName() , , JVM-. Class.getCanonicalName() , , Java-. Class.getSimpleName() . , :
Java 8 getName() , , . getCanonicalName() getSimpleName() , , . , .
Java 11 , . getCanonicalName() getSimpleName() 1,3 , getName(). , .
public String getName() {
String name = this.name;
if (name == null)
this.name = name = getName0();
return name;
}
// cache the name to reduce the number of calls into the VM
private transient String name;
private native String getName0();
getName() . getName(), .
public String getCanonicalName() {
if (isArray()) {
String canonicalName = getComponentType().getCanonicalName();
if (canonicalName != null)
return canonicalName + "[]";
else
return null;
}
if (isLocalOrAnonymousClass())
return null;
Class<?> enclosingClass = getEnclosingClass();
if (enclosingClass == null) { // top level class
return getName();
} else {
String enclosingName = enclosingClass.getCanonicalName();
if (enclosingName == null)
return null;
return enclosingName + "." + getSimpleName();
}
}
getCanonicalName() Java 10, , , . getSimpleName() . , Java 8 , Java. , , java.lang.Class, «» .
Java 8 32- JVM 112 , SoftReference reflection. reflection, , . Java 8 ReflectionData SoftReference. 32- JVM 32 , Reflection , 224 , 8 Reflection.
. - getSimpleName() getCanonicalName() . - , . ReflectionData. ReflectionData .
// reflection data that might get invalidated
// when JVM TI RedefineClasses() is called
private static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
. , JVMTI-, ReflectionData , ReflectionData.
private transient volatile SoftReference<ReflectionData<T>> reflectionData;
// Incremented by the VM on each call to JVM TI RedefineClasses()
// that redefines this class or a superclass.
private transient volatile int classRedefinedCount;
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
if (reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
return newReflectionData(reflectionData, classRedefinedCount);
}
-, SoftReference, . -, redefinedCount, . , int, .
, ReflectionData GC , classRedefinedCount .
private ReflectionData<T> newReflectionData(
SoftReference<ReflectionData<T>> oldReflectionData,
int classRedefinedCount) {
while (true) {
ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
// try to CAS it...
if (Atomic.casReflectionData(
this, oldReflectionData, new SoftReference<>(rd))) {
return rd;
}
// else retry
oldReflectionData = this.reflectionData;
classRedefinedCount = this.classRedefinedCount;
if (oldReflectionData != null &&
(rd = oldReflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
}
}
CAS-, , .
Java 11 ReflectionData :
private static class ReflectionData<T> {
volatile Field[] declaredFields;
…
volatile Class<?>[] interfaces;
+ // Cached names
+ String simpleName;
+ String canonicalName;
+ static final String NULL_SENTINEL = new String();
…
}
(, ), , ( - ). .
public String getCanonicalName() {
ReflectionData<T> rd = reflectionData();
String canonicalName = rd.canonicalName;
if (canonicalName == null) {
rd.canonicalName = canonicalName = getCanonicalName0();
}
return canonicalName == ReflectionData.NULL_SENTINEL ?
null : canonicalName;
}
Java 11 getCanonicalName() . getSimpleName(). , ReflectionData, , 1,3 .
Class.getConstructor
getConstructor(). :
public static class X {
public X() {}
}
public static class X1 {
public X1() {}
public X1(int p1) {}
public X1(int p1, int p2) {}
public X1(int p1, int p2, int p3) {}
public X1(int p1, int p2, int p3, int p4) {}
public X1(int p1, int p2, int p3, int p4, int p5) {}
}
public static class X2 {
public X2() {}
public X2(int p1) {}
public X2(int p1, int p2) {}
public X2(int p1, int p2, int p3) {}
public X2(int p1, int p2, int p3, int p4) {}
public X2(int p1, int p2, int p3, int p4, int p5) {}
public X2(int p1, int p2, int p3, int p4, int p5, int p6) {}
public X2(int p1, int p2, int p3, int p4, int p5, int p6, int p7) {}
public X2(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) {}
public X2(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9) {}
public X2(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10) {}
}
, :
@Benchmark
public Constructor<?> getConstructorX() throws NoSuchMethodException {
return X.class.getConstructor();
}
@Benchmark
public Constructor<?> getConstructorX1() throws NoSuchMethodException {
return X1.class.getConstructor();
}
@Benchmark
public Constructor<?> getConstructorX2() throws NoSuchMethodException {
return X2.class.getConstructor();
}
Java 8, , :
90 , JDK Java 9:
, . , . , :
. Java 8 , Java 9 . ?
getConstructor() getConstructor(), Java 8 :
private Constructor<T> getConstructor0(Class<?>[] parameterTypes, int which) throws NoSuchMethodException
{
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes, constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
. ReflectionData privateGetDeclaredConstructors(). find-first , . , reflection , , . , , , .
getParameterTypes(): . , ( Reflection API , , ).
, Constructor , :
public final class Constructor<T> extends Executable {
…
@Override
public Class<?>[] getParameterTypes() {
return parameterTypes.clone();
}
…
}
, , , , . . Class java.lang, Constructor java.lang.reflect, . , « », . Java 9 getConstructor0():
private Constructor<T> getConstructor0(Class<?>[] parameterTypes, int which) throws NoSuchMethodException
{
ReflectionFactory fact = getReflectionFactory();
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes, fact.getExecutableSharedParameterTypes(constructor))) {
return constructor;
}
}
throw new NoSuchMethodException(methodToString("<init>", parameterTypes));
}
« » ReflectionFactory:
package jdk.internal.reflect;
public class ReflectionFactory {
…
private final JavaLangReflectAccess langReflectAccess;
private ReflectionFactory() {
this.langReflectAccess = SharedSecrets.getJavaLangReflectAccess();
}
…
public Class<?>[] getExecutableSharedParameterTypes(Executable ex) {
return langReflectAccess.getExecutableSharedParameterTypes(ex);
}
…
}
, «». «» JavaLangReflect, java.base. jdk.internal , .
SharedSecrets, , jdk.internal.access , «» JDK, JavaLangReflect:
package jdk.internal.access;
public class SharedSecrets {
private static JavaLangReflectAccess javaLangReflectAccess;
public static void setJavaLangReflectAccess(JavaLangReflectAccess jlra) {
javaLangReflectAccess = jlra;
}
public static JavaLangReflectAccess getJavaLangReflectAccess() {
return javaLangReflectAccess;
}
…
}
JavaLangReflectAccess — , :
package jdk.internal.access;
/** An interface which gives privileged packages Java-level access to
internals of java.lang.reflect. */
public interface JavaLangReflectAccess {
/** Gets the shared array of parameter types of an Executable. */
public Class<?>[] getExecutableSharedParameterTypes(Executable ex);
…
}
AccessibleObject:
package java.lang.reflect;
public class AccessibleObject implements AnnotatedElement {
static {
// AccessibleObject is initialized early in initPhase1
SharedSecrets.setJavaLangReflectAccess(new ReflectAccess());
}
…
}
AccessibleObject JVM, .
— java.lang.reflect, :
package java.lang.reflect;
/** Package-private class implementing the
jdk.internal.access.JavaLangReflectAccess interface, allowing the java.lang
package to instantiate objects in this package. */
class ReflectAccess implements jdk.internal.access.JavaLangReflectAccess {
…
public Class<?>[] getExecutableSharedParameterTypes(Executable ex) {
return ex.getSharedParameterTypes();
}
…
}
, — :
:
, . , , Java .
9 , Java 9-16, , JDK, . , Java 8 , . !
— . , Joker 2020 , -. , .
— Java Champion -1 Java, JVM, concurrency, file-io memory Stack Overflow. Java-, , .