textos texto numeros numero metodo lista inicio definicion concatenar caracteres java string string-concatenation java-9 invokedynamic

numeros - concatenar textos java



¿Cómo se implementa la concatenación de cadenas en Java 9? (3)

Agregaré un poco de detalles aquí. La parte principal que se debe obtener es que la forma en que se realiza la concatenación de cadenas es una decisión de tiempo de ejecución, ya no es una hora de compilación . Por lo tanto, puede cambiar, lo que significa que compiló su código una vez contra java-9 y puede cambiar la implementación subyacente como quiera, sin la necesidad de volver a compilar.

Y el segundo punto es que en este momento hay 6 possible strategies for concatenation of String :

private enum Strategy { /** * Bytecode generator, calling into {@link java.lang.StringBuilder}. */ BC_SB, /** * Bytecode generator, calling into {@link java.lang.StringBuilder}; * but trying to estimate the required storage. */ BC_SB_SIZED, /** * Bytecode generator, calling into {@link java.lang.StringBuilder}; * but computing the required storage exactly. */ BC_SB_SIZED_EXACT, /** * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. * This strategy also tries to estimate the required storage. */ MH_SB_SIZED, /** * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. * This strategy also estimate the required storage exactly. */ MH_SB_SIZED_EXACT, /** * MethodHandle-based generator, that constructs its own byte[] array from * the arguments. It computes the required storage exactly. */ MH_INLINE_SIZED_EXACT }

Puede elegir cualquiera de ellos mediante un parámetro: -Djava.lang.invoke.stringConcat . Tenga en cuenta que StringBuilder sigue siendo una opción.

Como está escrito en JEP 280: Indicar concatenación de cadenas :

Cambie la String estática de bytecode String -concatenation generada por javac para utilizar llamadas invokedynamic a las funciones de la biblioteca JDK Esto permitirá futuras optimizaciones de String concatenación de String sin requerir más cambios en el bytecode emitido por javac .

Aquí quiero entender cuál es el uso de llamadas invokedynamic y cómo la concatenación de bytecode es diferente de invokedynamic .


Antes de entrar en los detalles de la implementación invokedynamic utilizada para la optimización de la concatenación de cadenas, en mi opinión, uno debe obtener algunos antecedentes sobre ¿Qué es invocado dinámico y cómo lo uso?

La instrucción invokedynamic simplifica y potencialmente mejora las implementaciones de compiladores y sistemas de tiempo de ejecución para lenguajes dinámicos en la JVM . Hace esto permitiendo que el implementador del lenguaje defina un comportamiento de vinculación personalizado con la instrucción invokedynamic que implica los siguientes pasos a continuación.

Probablemente trataría de llevarlo a cabo con los cambios que se llevaron a cabo para la implementación de la optimización de concatenación de cadenas.

  • Definición del método Bootstrap : - Con Java9, los métodos bootstrap para los sitios de llamada invokedynamic , para soportar la concatenación de cadenas principalmente makeConcat y makeConcatWithConstants se introdujeron con la implementación StringConcatFactory .

    El uso de invocados dinámicos proporciona una alternativa para seleccionar una estrategia de traducción hasta el tiempo de ejecución. La estrategia de traducción utilizada en StringConcatFactory es similar a la LambdaMetafactory como se introdujo en la versión anterior de Java. Además, uno de los objetivos del JEP mencionado en la pregunta es ampliar aún más estas estrategias.

  • Especificación de entradas constantes de agrupación : - Estos son los argumentos estáticos adicionales a la instrucción invokedynamic que no sea (1) MethodHandles.Lookup nombre mencionado en el sitio de llamadas dinámicas y (3) el objeto MethodType , la firma de tipo resuelta del sitio de llamadas dinámicas.

    Ya están vinculados durante el enlace del código. En tiempo de ejecución, se ejecuta el método bootstrap y se vincula en el código real haciendo la concatenación. invokedynamic llamada invokedynamic con una llamada invokestatic 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 invocada dinámica : - Ofrece las facilidades para un enlace diferido , al proporcionar los medios para iniciar el destino de la llamada una vez, durante la invocación inicial. La idea concreta para la optimización aquí es reemplazar todo el baile StringBuilder.append con una simple llamada invokedynamic a java.lang.invoke.StringConcatFactory , que aceptará los valores en la necesidad de concatenación.

La propuesta Indify String Concatenation establece con un ejemplo la evaluación comparativa de la aplicación con Java9 donde se compila un método similar al compartido por @TJ Crowder y la diferencia en el código de bytes es bastante visible entre la implementación variable.


La forma "antigua" generaba un montón de operaciones orientadas a StringBuilder . Considere este programa:

public class Example { public static void main(String[] args) { String result = args[0] + "-" + args[1] + "-" + args[2]; System.out.println(result); } }

Si compilamos eso con JDK 8 o anterior y luego usamos javap -c Example para ver el código de bytes, vemos algo como esto:

public class Example { public Example(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V 7: aload_0 8: iconst_0 9: aaload 10: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 13: ldc #5 // String - 15: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 18: aload_0 19: iconst_1 20: aaload 21: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: ldc #5 // String - 26: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: aload_0 30: iconst_2 31: aaload 32: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 38: astore_1 39: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 42: aload_1 43: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 46: return }

Como puede ver, crea un StringBuilder y utiliza append . Esto es famoso como bastante ineficiente, ya que la capacidad predeterminada del búfer incorporado en StringBuilder es de solo 16 caracteres, y el compilador no tiene forma de asignar más por adelantado, por lo que termina teniendo que reasignarse. 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 que sean más eficientes).

Veamos qué genera Java 9:

public class Example { public Example(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 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 // Field java/lang/System.out:Ljava/io/PrintStream; 18: aload_1 19: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 22: return }

Oh mi pero eso es más corto. :-) Hace una sola llamada a makeConcatWithConstants desde StringConcatFactory , que dice esto en su Javadoc:

Métodos para facilitar la creación de métodos de concatenación de cadenas, que pueden usarse para concatenar eficientemente un número conocido de argumentos de tipos conocidos, posiblemente después de la adaptación de tipos y la evaluación parcial de los argumentos. Estos métodos se usan típicamente como métodos de arranque para sitios de llamadas invokedynamic , para admitir la función de concatenación de cadenas del lenguaje de programación Java.