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 bytecodeString
-concatenation generada porjavac
para utilizar llamadasinvokedynamic
a las funciones de la biblioteca JDK Esto permitirá futuras optimizaciones deString
concatenación deString
sin requerir más cambios en el bytecode emitido porjavac
.
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óninvokedynamic
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 principalmentemakeConcat
ymakeConcatWithConstants
se introdujeron con la implementaciónStringConcatFactory
.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 laLambdaMetafactory
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 objetoMethodType
, 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
llamadainvokedynamic
con una llamadainvokestatic
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 llamadainvokedynamic
ajava.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.