Как написано в JEP 280: Указать конкатенацию строк :
Измените
String
последовательность байт-кода статической конкатенации, созданную сjavac
помощьюinvokedynamic
вызовов функций библиотеки JDK. Это позволит в будущем оптимизироватьString
конкатенацию, не требуя дальнейших изменений байт-кода, создаваемогоjavac
.
Здесь я хочу понять, что такое использование invokedynamic
вызовов и чем отличается конкатенация байт-кода от invokedynamic
?
java
string
string-concatenation
java-9
invokedynamic
Мохит Тьяги
источник
источник
Ответы:
«Старый» способ выводит кучу
StringBuilder
-ориентированных операций. Рассмотрим эту программу:Если мы скомпилируем это с помощью JDK 8 или более ранней
javap -c Example
версии, а затем воспользуемся им для просмотра байт-кода, мы увидим что-то вроде этого:Как видите, он создает
StringBuilder
и используетappend
. Это известно довольно неэффективно, так как емкость встроенного буфера по умолчаниюStringBuilder
составляет всего 16 символов, и компилятор не знает, как выделить больше заранее, поэтому ему приходится перераспределять. Это также набор вызовов методов. (Обратите внимание, что JVM иногда может обнаруживать и переписывать эти шаблоны вызовов, чтобы сделать их более эффективными.)Посмотрим, что генерирует Java 9:
О боже, но это короче. :-) Он выполняет единственный вызов
makeConcatWithConstants
fromStringConcatFactory
, в Javadoc которого говорится следующее:источник
+=
в своем цикле for. Я сказал им, что это зависит от обстоятельств, но давайте не будем забывать, что они могут найти лучший способ связать конкатенацию в будущем. Ключевая строка - это действительно предпоследняя строка:So by being smart, you have caused a performance hit when Java got smarter than you.
invokedynamic
позволяет выбирать разные стратегии конкатенации во время выполнения и связывать их при первом вызове без дополнительных затрат на вызов метода и таблицу отправки при каждом вызове; подробнее в статье Николая здесь и в JEP .Object
, но тогда вам придется боксировать все примитивы ... (что Николай освещает в своей превосходной статье, кстати.)String.concat(String)
метод, реализация которого создает массив результирующих строк на месте. Преимущество становится спорным, когда мы должны вызыватьtoString()
произвольные объекты. Аналогично, при вызове метода, принимающего массив, вызывающая сторона должна создать и заполнить массив, что снижает общую выгоду. Но теперь это не имеет значения, поскольку новое решение в основном то, что вы рассматривали, за исключением того, что оно не имеет накладных расходов, не требует создания массива, а серверная часть может генерировать оптимизированные обработчики для определенных сценариев.Прежде чем вдаваться в подробности
invokedynamic
реализации, используемой для оптимизации конкатенации строк, на мой взгляд, необходимо получить некоторые сведения о том, что invokedynamic и как его использовать?Я бы, вероятно, попытался рассказать вам об изменениях, которые были внесены для реализации оптимизации конкатенации строк.
Определение метода Bootstrap : - С Java9, методов начальной загрузки для
invokedynamic
сайтов вызова, чтобы поддерживать конкатенацию в первую очередьmakeConcat
иmakeConcatWithConstants
были введены сStringConcatFactory
реализацией.Использование invokedynamic предоставляет альтернативу для выбора стратегии перевода до времени выполнения. Стратегия перевода, используемая в
StringConcatFactory
, аналогична стратегии,LambdaMetafactory
представленной в предыдущей версии Java. Кроме того, одной из целей JEP, упомянутых в вопросе, является дальнейшее расширение этих стратегий.Указание постоянных записей пула : - это дополнительные статические аргументы
invokedynamic
инструкции, кроме (1)MethodHandles.Lookup
объекта, который является фабрикой для создания дескрипторов методов в контекстеinvokedynamic
инструкции, (2)String
объекта, имя метода, упомянутого в динамическом вызове site и (3)MethodType
объект, сигнатура разрешенного типа динамического сайта вызова.Уже есть ссылки во время связывания кода. Во время выполнения запускается метод начальной загрузки и связывается с фактическим кодом, выполняя конкатенацию. Он перезаписывает
invokedynamic
вызов соответствующимinvokestatic
вызовом. Это загружает постоянную строку из пула констант, статические аргументы метода начальной загрузки используются для передачи этих и других констант прямо в вызов метода начальной загрузки.Использование инструкции invokedynamic : - Это предлагает средства для ленивого связывания, предоставляя средства для однократной начальной загрузки цели вызова во время первоначального вызова. Конкретная идея оптимизации здесь состоит в том, чтобы заменить весь
StringBuilder.append
танец простымinvokedynamic
вызовомjava.lang.invoke.StringConcatFactory
, который примет значения, требующие объединения.В предложении Indify String Concatenation приводится пример тестирования приложения с Java9, где компилируется аналогичный метод, используемый @TJ Crowder, и разница в байт-коде довольно заметна между различными реализациями.
источник
Я немного добавлю здесь немного деталей. Главное, что нужно понять, это то, как выполняется конкатенация строк, это решение во время выполнения, а не во время компиляции . Таким образом, он может измениться, что означает, что вы один раз скомпилировали свой код для java-9, и он может изменить базовую реализацию, как пожелает, без необходимости повторной компиляции.
И второй момент: на данный момент это
6 possible strategies for concatenation of String
:Вы можете выбрать любого из них с помощью параметра:
-Djava.lang.invoke.stringConcat
. Обратите внимание, чтоStringBuilder
это все еще вариант.источник