¿Cómo se implementa la concatenación de cadenas en Java 9?
Como está escrito en JEP 280:
Cambie la secuencia estática
String
-concatenación de bytecode generada porjavac
para usar llamadasinvokedynamic
a las funciones de la biblioteca JDK. Esto permitirá optimizaciones futuras de la concatenaciónString
sin requerir cambios adicionales en el bytecode emmited porjavac
.
Aquí quiero entender cuál es el uso de llamadas invokedynamic
y cómo la concatenación de bytes es diferente de invokedynamic
?
3 answers
La forma "antigua" genera 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 bytecode, 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 puedes ver, crea un StringBuilder
y usa append
. Esto es famoso bastante ineficiente ya que la capacidad predeterminada del búfer incorporado en StringBuilder
es de solo 16 caracteres, y no hay forma de que el compilador sepa asignar más por adelantado, por lo que termina tener que reasignar. También es un montón de llamadas de método. (Tenga en cuenta que la JVM puede a veces detectar y reescribir estos patrones de llamadas para hacerlos más eficientes, sin embargo.)
Veamos lo que 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, 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 se pueden utilizar para concatenar un número conocido de argumentos de tipos conocidos, posiblemente después de la adaptación de tipos y la evaluación parcial de argumentos. Estos métodos se usan típicamente como métodos bootstrap para
invokedynamic
sitios de llamadas, para soportar la característica concatenación de cadenas del Lenguaje de programación Java.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-02 16:22:09
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 algo de fondo sobre ¿Qué es invokedynamic y cómo lo uso?
El
invokedynamic
la instrucción simplifica y potencialmente mejora las implementaciones de compiladores y sistemas de tiempo de ejecución para lenguajes dinámicos en la JVM . Se hace esto al permitir que el implementador de lenguaje defina personalizado comportamiento de enlace con la instruccióninvokedynamic
que implica los siguientes pasos.
Probablemente intentaría guiarlo a través de estos con los cambios que se trajeron para la implementación de la optimización de concatenación de cadenas.
-
Definiendo el Método Bootstrap:- Con Java9, los métodos bootstrap para
invokedynamic
llaman a sitios, para soportar la concatenación de cadenas principalmentemakeConcat
ymakeConcatWithConstants
eran introducido con elStringConcatFactory
aplicació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
StringConcatFactory
es similar a laLambdaMetafactory
como se introdujo en la versión anterior de Java. Además, uno de los objetivos del PEC mencionado en la pregunta es ampliar aún más estas estrategias. -
Especificar Entradas de Grupos Constantes:- Estos son los argumentos estáticos adicionales a la instrucción
invokedynamic
distintos de (1)MethodHandles.Lookup
objeto que es una fábrica para crear manejadores de método en el contexto de la instruccióninvokedynamic
, (2) un objetoString
, el nombre del método mencionado en el sitio de llamada dinámica y (3) elMethodType
objeto, la firma de tipo resuelta del sitio de llamada dinámica.Ya hay enlaces durante el enlace del código. En tiempo de ejecución, el método bootstrap se ejecuta y enlaza en el código haciendo la concatenación. Reescribe la llamada
invokedynamic
con una llamadainvokestatic
apropiada. Esto carga la cadena constante del grupo de constantes, los args estáticos del método bootstrap se aprovechan para pasar estas y otras constantes directamente a la llamada al método bootstrap. Usando la Instrucción invokedynamic:- 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 toda la danza
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 el benchmarking de la aplicación con Java9 donde se compila un método similar al compartido por @T. J. Crowder y la diferencia en el bytecode es bastante visible aplicación.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-19 07:33:09
Voy a añadir un poco de detalles aquí. La parte principal a obtener es que cómo se realiza la concatenación de cadenas es una decisión en tiempo de ejecución , no una 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 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 a través de un parámetro : -Djava.lang.invoke.stringConcat
. Observe que StringBuilder
sigue siendo una opción.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-01 19:38:38