Como está escrito en JEP 280: Indique la concatenación de cadenas :
Cambie la
Stringsecuencia de código de bytes de concatenación estática generada porjavacpara usarinvokedynamicllamadas a las funciones de la biblioteca JDK. Esto permitirá futuras optimizaciones deStringconcatenación sin requerir más cambios en el código de bytes emitido porjavac.
Aquí quiero entender cuál es el uso de las invokedynamicllamadas y en qué se diferencia la concatenación de códigos de bytes invokedynamic.
java
string
string-concatenation
java-9
invokedynamic
Mohit Tyagi
fuente
fuente

Respuestas:
La forma "antigua" genera un montón de
StringBuilderoperaciones orientadas. Considere este programa:Si compilamos eso con JDK 8 o anterior y luego usamos
javap -c Examplepara ver el código de bytes, vemos algo como esto:Ejemplo de clase pública { Ejemplo público (); Código: 0: aload_0 1: invocarespecial # 1 // Método java / lang / Object. "<init>" :() V 4: regreso public static void main (java.lang.String []); Código: 0: nuevo # 2 // clase java / lang / StringBuilder 3: dup 4: invokespecial # 3 // Método java / lang / StringBuilder. "<init>" :() V 7: aload_0 8: iconst_0 9: aaload 10: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder; 13: ldc # 5 // Cadena - 15: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder; 18: aload_0 19: iconst_1 20: aaload 21: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder; 24: ldc # 5 // Cadena - 26: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder; 29: aload_0 30: iconst_2 31: aaload 32: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder; 35: invokevirtual # 6 // Método java / lang / StringBuilder.toString :() Ljava / lang / String; 38: astore_1 39: getstatic # 7 // Campo java / lang / System.out: Ljava / io / PrintStream; 42: aload_1 43: invokevirtual # 8 // Método java / io / PrintStream.println: (Ljava / lang / String;) V 46: regreso }Como puede ver, crea
StringBuildery usaappend. Esto es bastante ineficiente ya que la capacidad predeterminada del búfer incorporadoStringBuilderes de solo 16 caracteres, y el compilador no tiene forma de saber que debe asignar más por adelantado, por lo que termina teniendo que reasignar. También es un montón de llamadas a métodos. (Sin embargo, tenga en cuenta que la JVM a veces puede detectar y reescribir estos patrones de llamadas para hacerlos más eficientes).Veamos lo que genera Java 9:
Ejemplo de clase pública { Ejemplo público (); Código: 0: aload_0 1: invocarespecial # 1 // Método java / lang / Object. "<init>" :() V 4: regreso public static void main (java.lang.String []); Código: 0: aload_0 1: iconst_0 2: aaload 3: aload_0 4: iconst_1 5: aaload 6: aload_0 7: iconst_2 8: aaload 9: invokedynamic # 2, 0 // InvokeDynamic # 0: makeConcatWithConstants: (Ljava / lang / String; Ljava / lang / String; Ljava / lang / String;) Ljava / lang / String; 14: astore_1 15: getstatic # 3 // Campo java / lang / System.out: Ljava / io / PrintStream; 18: aload_1 19: invokevirtual # 4 // Método java / io / PrintStream.println: (Ljava / lang / String;) V 22: regreso }Oh Dios, pero eso es más corto. :-) Hace una sola llamada a
makeConcatWithConstantsfromStringConcatFactory, que dice esto en su Javadoc:fuente
+=en su bucle for. Les dije que depende, pero no olvidemos que podrían encontrar una mejor manera de encadenar concat en algún momento en el futuro. La línea clave es realmente la penúltima línea:So by being smart, you have caused a performance hit when Java got smarter than you.invokedynamicpermite elegir diferentes estrategias de concatenación en tiempo de ejecución y enlazarlas en la primera invocación, sin la sobrecarga de una llamada de método y una tabla de despacho en cada invocación; más en el artículo de nicolai aquí y en la JEP .Object, pero luego tendrías que encasillar todos los primitivos ... (que Nicolai cubre en su excelente artículo, por cierto)String.concat(String)método ya existente cuya implementación está creando la matriz de la cadena resultante en el lugar. La ventaja se vuelve discutible cuando tenemos que invocartoString()objetos arbitrarios. Del mismo modo, al llamar a un método que acepta una matriz, la persona que llama tiene que crear y completar la matriz, lo que reduce el beneficio general. Pero ahora, es irrelevante, ya que la nueva solución es básicamente lo que estaba considerando, excepto que no tiene una sobrecarga de boxeo, no necesita creación de matrices y el backend puede generar controladores optimizados para escenarios particulares.Antes de entrar en los detalles de la
invokedynamicimplementación utilizada para la optimización de la concatenación de cadenas, en mi opinión, uno debe obtener algunos antecedentes sobre ¿Qué es invokedynamic y cómo lo uso?Probablemente trataría de explicarle estos cambios con los cambios que se trajeron para la implementación de la optimización de la concatenación de cadenas.
Definición del método Bootstrap : - Con Java9, los métodos bootstrap para
invokedynamicsitios de llamadas, para admitir la concatenación de cadenas principalmentemakeConcatymakeConcatWithConstantsse introdujeron con laStringConcatFactoryimplementación.El uso de invokedynamic proporciona una alternativa para seleccionar una estrategia de traducción hasta el tiempo de ejecución. La estrategia de traducción utilizada en
StringConcatFactoryes similar a laLambdaMetafactoryintroducida en la versión anterior de Java. Además, uno de los objetivos de la JEP mencionado en la pregunta es ampliar aún más estas estrategias.Especificar entradas de grupo constante : - Estos son los argumentos estáticos adicionales a la
invokedynamicinstrucción que no sean (1)MethodHandles.Lookupobjeto que es una fábrica para crear identificadores de métodos en el contexto de lainvokedynamicinstrucción, (2) unStringobjeto, el nombre del método mencionado en la llamada dinámica sitio y (3) elMethodTypeobjeto, la firma de tipo resuelto del sitio de llamada dinámica.Ya están vinculados durante la vinculación del código. En tiempo de ejecución, el método bootstrap se ejecuta y enlaza en el código real haciendo la concatenación. Reescribe la
invokedynamicllamada con unainvokestaticllamada apropiada . Esto carga la cadena constante del grupo constante, los argumentos estáticos del método bootstrap se aprovechan para pasar estas y otras constantes directamente a la llamada al método bootstrap.Uso de la instrucción dinámica invocada : - Esto ofrece las facilidades para un enlace perezoso, proporcionando los medios para arrancar el destino de la llamada una vez, durante la invocación inicial. La idea concreta para la optimización aquí es reemplazar todo el
StringBuilder.appendbaile con una simpleinvokedynamicllamada ajava.lang.invoke.StringConcatFactory, que aceptará los valores en la necesidad de concatenación.La propuesta de Indify String Concatenation establece con un ejemplo la evaluación comparativa de la aplicación con Java9 donde un método similar al compartido por @TJ Crowder y la diferencia en el es bastante visible entre las diferentes implementaciones.
fuente
Agregaré algunos detalles aquí. La parte principal es que la forma en que se realiza la concatenación de cadenas es una decisión en tiempo de ejecución, ya no en tiempo de compilación . Por lo tanto, puede cambiar, lo que significa que ha compilado su código una vez contra java-9 y puede cambiar la implementación subyacente como le plazca, sin la necesidad de volver a compilar.
Y el segundo punto es que de momento existen
6 possible strategies for concatenation of String:Usted puede elegir cualquiera de ellos a través de un parámetro:
-Djava.lang.invoke.stringConcat. Tenga en cuenta queStringBuildersigue siendo una opción.fuente