Короткая версия: Java использует invokedynami c для объединения строк, начиная с Java 9.
Давайте разберем это немного:
Invokedynami c имеет два шага :
- Когда инструкция вызывается в первый раз, вызывается метод bootstrap. Когда он вернется, сайт вызова будет связан с результатом метода bootstrap.
- Последующие вызовы будут напрямую вызывать цель MethodHandle .
CallSite является просто держателем для этого MethodHandle . В зависимости от используемого подкласса CallSite
сайт может быть позднее перекомпонован.
Если мы посмотрим на инструкцию, в конце мы увидим следующее:
#0:makeConcatWithConstants:(I)Ljava/lang/String;
Первая часть (#0
) означает: Bootstrap метод # 0.
Вторая часть - это имя, которое передается методу bootstrap и может или не может использоваться там.
Третья часть - тип метода итоговой цели. В нашем случае: метод, который принимает int
и возвращает java.lang.String
.
Если мы теперь посмотрим на bootstrap метод # 0, мы увидим ссылку на метод, здесь на StringConcatFactory.makeConcatWithConstants (...) . Мы также видим, что есть дополнительный аргумент: String "text + String: \u0001"
.
Задача метода bootstrap теперь состоит в том, чтобы вернуть MethodHandle (внутри CallSite), который в этом случае выполняет конкатенацию строк. Но как это происходит для конкатенации строк (StringBuilder, String.format, вращение байт-кода, цепочка MethodHandles ...) не имеет значения для фактического класса. Нужно только объединить строки.
Давайте попробуем эмулировать это поведение вручную. В конце концов, метод bootstrap - это обычный метод Java:
public static void main(String[] args) throws Throwable {
CallSite cs = StringConcatFactory.makeConcatWithConstants(MethodHandles.lookup(),
"makeConcatWithConstants", MethodType.methodType(String.class, int.class),
"text + String: \u0001");
int x = 2;
String result = (String) cs.dynamicInvoker().invokeExact(x);
System.out.println(result);
x = 3;
result = (String) cs.dynamicInvoker().invokeExact(x);
System.out.println(result);
}
(ВМ делает еще кое-что, например, запоминает результат и больше не будет вызывать метод bootstrap, но для нашего небольшого примера это достаточно хорошо).
На данный момент мы можем заглянуть под капот о том, как метод bootstrap выполняет свою работу.
Получается: вы можете настройте виртуальную машину для использования различных стратегий.
И она использует свое привилегированное положение внутри java.base
для доступа к частному конструктору пакета для java .lang.String, который не копирует массив - что безопасно, если содержимое впоследствии не изменяется.
Стратегия по умолчанию - MethodHandle Chaining.
Хорошая новость: если кто-то напишет в какой-то момент лучшую стратегию, ваша программа выиграет от нее - без перекомпиляции.