Cuando leí el código de bytes jvm que, a partir de una pequeña función de Java, descubrí que cuando se calcula una nueva variable local en la pila de operandos, suponiendo que se almacenará en la tabla de variables locales, pero generalmente se cargará en la pila de operandos inmediatamente (solo en términos de bytecode literalmente). No entiendo bien la operación, ¿es una operación innecesaria?
El compilador de Java tiende a compilar las cosas de una manera muy simple y directa, dejando la optimización al JIT.
Por ejemplo, si escribe x *= 3; x *= 4; , probablemente obtendrá un código de bytes en la línea de
iload_1 iconst_3 imul istore_1 iload_1 iconst_4 imul istore_1En teoría, el compilador podría darse cuenta de que el par almacenar/cargar es redundante y eliminarlo. Pero hay varias razones para no hacerlo: 1) esto agrega mucha complejidad sin beneficio ya que el JIT optimiza todo de todos modos 2) dificulta la depuración, ya que ya no tiene acceso a los valores de todas las variables locales 3) si de alguna manera se lanza una excepción en medio de esta expresión, las variables locales tendrán los valores incorrectos.
Mirando el código de dspin
Method void dspin() 0 dconst_0 // Push double constant 0.0 1 dstore_1 // Store into local variables 1 and 2 2 goto 9 // First time through don't increment 5 dload_1 // Push local variables 1 and 2 6 dconst_1 // Push double constant 1.0 7 dadd // Add; there is no dinc instruction 8 dstore_1 // Store result in local variables 1 and 2 9 dload_1 // Push local variables 1 and 2 10 ldc2_w #4 // Push double constant 100.0 13 dcmpg // There is no if_dcmplt instruction 14 iflt 5 // Compare and loop if less than (i < 100.0) 17 return // Return void when done La única load que sigue a store está en el desplazamiento 9. Puede ver que se puede llegar al desplazamiento 9 por dos caminos diferentes: (1) desde el desplazamiento 2 con goto 9 ; y (2) secuencialmente desde el desplazamiento 8
dload_1 el valor de las variables locales 1 y 2 en la pila de operandos (dos variables debido a double ): en el caso (1) cuando intenta ingresar al ciclo por primera vez, y en el caso (2) cuando intenta ingresar al ciclo en momentos posteriores.
Curiosamente, en este ejemplo, si elimina todas las store y load , el comportamiento del programa no cambiará. Sin embargo, el compilador de Java por lo general no intenta ser inteligente. Compila código Java más o menos directamente. En este caso la variable local i corresponde directamente a las variables locales 1 y 2.
Consulte Optimización mediante el compilador de Java para obtener más información.
Mira, cada operación en JVM se realiza en la pila de operandos. Entonces, siempre que tenga que realizar cualquier operación en una variable, primero debe cargar (empujar) en la pila de operandos mediante el comando de carga y luego realizar la operación.
Esta es la razón por la que a store le sigue la instrucción de carga en bytecode.