После некоторых исследований, похоже, это зависит от того, что создание лямбда-выражений выполняется через invokedynamic и то, что вы видите, это побочный эффект от того, как ведет себя invokedynamic на JVM Оракула.
Декомпиляция ваши x1()
и x2()
методы:
public static java.util.concurrent.Callable x1();
Code:
stack=1, locals=1, args_size=0
0: getstatic #2 // Field o:Ljava/lang/Object;
3: astore_0
4: aload_0
5: invokedynamiC#3, 0 // InvokeDynamiC#0:call:(Ljava/lang/Object;)Ljava/util/concurrent/Callable;
10: areturn
public static java.util.concurrent.Callable x2();
Code:
stack=1, locals=0, args_size=0
0: invokedynamiC#4, 0 // InvokeDynamiC#1:call:()Ljava/util/concurrent/Callable;
5: areturn
Соответствующий раздел Constant бассейна:
#3 = InvokeDynamic #0:#37 // #0:call:(Ljava/lang/Object;)Ljava/util/concurrent/Callable;
#4 = InvokeDynamic #1:#39 // #1:call:()Ljava/util/concurrent/Callable;
BootstrapMethods:
0: #34 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#35()Ljava/lang/Object;
#36 invokestatic Test.lambda$x1$0:(Ljava/lang/Object;)Ljava/lang/Object;
#35()Ljava/lang/Object;
1: #34 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#35()Ljava/lang/Object;
#38 invokestatic Test.lambda$x2$1:()Ljava/lang/Object;
#35()Ljava/lang/Object;
Как пояснялось here:
Поскольку каждый invokedynamic ссылка инструкции (в целом) к другому сайту вызова (у нас есть два места вызова, один для каждой функции Xn), постоянный кэш пула должен содержать отдельную запись для каждого invokedynamic инструкции , (Другие инструкции ВЫЗОВА может обмениваться записями кэша CP, если они используют ту же символическую ссылку в постоянного пула.)
Запись кэша Constant Pool («СРС»), когда проблема будет решено, имеет один или два слово метаданных и/или информации о смещении.
Для invokedynamic разрешенный CPCE содержит указатель метода * к методу конкретного адаптера , обеспечивающий точное поведение вызова. Существует также ссылочный параметр, связанный с сайтом вызова, который называется приложением, которое хранится в массиве разрешенных_ресурсов для CPCE.
Метод называется адаптером, потому что (вообще говоря) он перетасовывает аргументы, извлекает дескриптор целевого метода с сайта и вызывает дескриптор метода.
Дополнительный ссылочный параметр называется приложением, потому что он содержит , присоединенный к списку аргументов, когда выполнена команда invokedynamic .
Обычно приложение является ссылкой CallSite, созданной методом начальной загрузки , но JVM не заботится об этом. Пока метод адаптера в CPCE знает, что делать с приложением, хранящимся в с CPCE, все хорошо.
В качестве углового случая, если значение приложения равно null, оно не перетаскивается в все, и метод адаптера не должен ожидать дополнительного аргумента. Адаптер в этом случае может быть постоянно ссылка на статический метод с подписью, соответствующей инструкции invokedynamic . Это фактически превратило invokedynamic в простой вызов invokestatic.Многие другие такие оптимизации сокращения прочности возможны .
Я интерпретация, что «Это будет фактически очереди» в том смысле, что в таких условиях (адаптер без параметров) invokedynamic будет эффективно вести себя, как и invokestatic вызова, и адаптер будет кэшировать и повторно.
Все это относится к JVM Oracle, но я подозреваю, что в отношении этого аспекта это один из наиболее очевидных вариантов, и я ожидал увидеть что-то подобное даже в других реализациях jvm.
Кроме того, отметьте это хорошим answer для более чистой перефразировки этой цитаты, лучше, чем я мог бы это объяснить.
Не ответ на ваш вопрос, но я предполагаю, что каждый раз, когда вы вызываете 'x1()', он создает новый объект 'x', что, очевидно, делает лямбду другой. Вопреки этому, 'x2' всегда совпадает с возвратом статического объекта, который не изменяется. – Crembo
@Crembo интересно догадаться, но если вы измените его на '() -> новый Object()', он также вернет тот же лямбда-объект. – Andremoniy
@Crembo BTW, в 'x1()' нет новых объектов – Andremoniy