tamaño que memoria direcciones dinamica diferencia java

java - que - ¿Las variables locales no utilizadas en un método adquieren memoria en JVM?



stack memoria dinamica (5)

Este es el tipo de pregunta que vale la pena examinar con javap.

public class Foo { public int bar(){ System.out.println("foo"); return 8; } public int foo(){ int x; System.out.println("foo"); return 8; } }

Observe que la diferencia entre foo () y bar () es que una declara una variable local x y la otra no.

Ahora mire el código jvm (use javap -v Foo para ver esto en su máquina)

public int bar(); descriptor: ()I flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String foo 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: bipush 8 10: ireturn LineNumberTable: line 6: 0 line 7: 8 public int foo(); descriptor: ()I flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String foo 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: bipush 8 10: ireturn LineNumberTable: line 12: 0 line 13: 8 }

Lo interesante es que la salida línea por línea es idéntica, pero los locales para la barra es 1, y para foo es 2. Así que parece que el espacio está realmente asignado para x, aunque la salida del compilador nunca se usa eso.

ACTUALIZACIÓN: Investigando un poco más sobre esto, encuentro esta página que me sugiere que, aunque el bytecode compilado implica que el espacio está asignado para x, de hecho puede ser optimizado por el jvm. Desafortunadamente, no encuentro una descripción completa de las optimizaciones realizadas. En particular, el capítulo de documentación de JVM sobre compiling no menciona la eliminación de variables no utilizadas de la pila. Entonces, a menos que se realicen más descubrimientos, mi respuesta sería que depende de la implementación, pero parece ser el tipo de optimización que cualquier compilador que se precie realizaría. Tenga en cuenta también que no importa mucho que se trate de una variable local en lugar de un campo; de hecho, las variables locales son las que tienen más probabilidades de optimizarse, ya que son las más fáciles de analizar y eliminar. (precisamente porque son locales)

Encontré esta publicación en SO ¿Las variables de instancia primitivas sin inicializar usan memoria?

Indica "En Java, ¿cuesta la memoria declarar una variable de instancia de nivel de clase sin inicializarla? Por ejemplo: ¿Int i; utiliza alguna memoria si no la inicializo con i = 5 ;?"

Mi pregunta es qué ocurre con las variables locales, si tengo un método foo ()

public int foo(){ int x; //Write code which does not use/initialize x }

¿La variable local x ocupará memoria?

Editar

La respuesta de Jon es

ACTUALIZACIÓN: Investigando un poco más sobre esto, encuentro esta página que me sugiere que, aunque el bytecode compilado implica que el espacio está asignado para x, de hecho puede ser optimizado por el jvm. Desafortunadamente, no encuentro una descripción completa de las optimizaciones realizadas. En particular, el capítulo de documentación de JVM sobre compilación no menciona la eliminación de variables no utilizadas de la pila. Entonces, a menos que se realicen más descubrimientos, mi respuesta sería que depende de la implementación, pero parece ser el tipo de optimización que cualquier compilador que se precie realizaría. Tenga en cuenta también que no importa mucho que se trate de una variable local en lugar de un campo; de hecho, las variables locales son las que tienen más probabilidades de optimizarse, ya que son las más fáciles de analizar y eliminar. (precisamente porque son locales)

Veamos si podemos encontrar más evidencias que apoyen esto.


Expandiéndose ligeramente en el testcase de @JonKiparsky:

public class StackSlotTest { public int bar(){ int a; a = 5; System.out.println("foo"); return a; } public int foo(){ int x; int a; a = 5; System.out.println("foo"); return a; } }

Agregué la variable a a ambos métodos, y agregue un conjunto y uso de ella en ambos.

public int bar(); Code: 0: iconst_5 1: istore_1 2: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 5: ldc #3 // String foo 7: invokevirtual #4 // Method java/io/PrintStream.printl n:(Ljava/lang/String;)V 10: iload_1 11: ireturn

Arriba se ve que el bytecode iload_1 carga el valor de a para ser devuelto. Se hace referencia a la segunda ranura de pila. (El primero es el puntero de this ).

public int foo(); Code: 0: iconst_5 1: istore_2 2: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 5: ldc #3 // String foo 7: invokevirtual #4 // Method java/io/PrintStream.printl n:(Ljava/lang/String;)V 10: iload_2 11: ireturn

En este caso, el valor de a se carga con iload_2 , para acceder a la tercera ranura, porque la segunda ranura está ocupada (más o menos) por la variable x (totalmente sin usar).


La forma en que una variable primitiva se almacena en una variable local (en código de bytes) es con:

istore (para valores int ), dstore (para valores double ), fstore (para valores float ), etc.

Cada uno de ellos abre la parte superior de la pila de operadores y la escribe en la variable local numerada Var . Por lo tanto, un operador debe empujar el valor que necesita ser almacenado, en la parte superior de la pila del operador, justo antes de la operación de la store . Trabajan en pareja.

Entonces, si haces algo como:

int i; //... i = 12; //...

Su compilador empujará el número entero 12 en la parte superior de la pila de operadores y luego sacará este valor y lo escribirá en una variable local solo en el momento de la inicialización de la variable.

//... bipush 12 istore_1 1

Si nunca inicializa su variable, el compilador no puede empujar un valor en la parte superior de la pila del operador, por lo que la operación de almacenamiento es imposible. no es una optimización del compilador, es solo la forma en que funciona el bytecode. Por lo tanto, su compilador simplemente elimina su línea.

Tomemos este sencillo ejemplo:

public static void main(String[] args) { int i; int j = 10; System.out.println(j); i = 12; System.out.println(i); }

Y mira cuando se inicializa la variable local i :

public static void main(String[] p0) { bipush 10 istore_2 2 getstatic PrintStream System.out iload_2 2 invokevirtual void PrintStream.println(int) bipush 12 istore_1 1 getstatic PrintStream System.out iload_1 1 invokevirtual void PrintStream.println(int) return }

Puede observar que la variable local # 2 se usa antes que la variable local # 1.

Tomemos este último ejemplo:

public static void main(String[] args) { int i; int j = 10; System.out.println(j); }

El bytecode sería:

public static void main(String[] p0) { bipush 10 istore_2 2 getstatic PrintStream System.out iload_2 2 invokevirtual void PrintStream.println(int) return }

La variable local 1 nunca se usa y algo se almacena en la variable local 2 . Debido a eso, se asignarían 2 x 32 bits aquí incluso si la variable local 1 no se utiliza.

Edición: (del comentario de Hot Licks)

El intérprete de JVM asignará tantas ranuras locales como se especifiquen en el encabezado del método. Se asignan si se utilizan o no. Los valores largos y dobles obtienen dos ranuras (aunque las ranuras son de 64 bits y solo una se usa en la mayoría de las implementaciones modernas).

Una variable local int no inicializada requeriría 32 bits aquí, por ejemplo.


Las variables de nivel de clase / nivel de instancia se inicializarán automáticamente a sus valores predeterminados . Entonces, sí, ocuparán algo de espacio cuando se inicialice / cree una clase, respectivamente.

En lo que concierne a las variables locales del método, No, si solo se declaran pero no se inicializan, entonces no usarán ningún espacio, son tan buenas como las que el compilador ignora.

Si tu código fuera este:

public static void main(String[] args) { int i; // ignored int j = 5; String s = "abc"; String sNull; // ignored }

Código de byte:

LocalVariableTable: Start Length Slot Name Signature 0 6 0 args [Ljava/lang/String; 2 4 2 j I 5 1 3 s Ljava/lang/String; }


Razón: compilación de tiempo de optimización.

Como usted codifica:

public int foo(){ int x; //Write code which does not use/initialize x }

La mayoría de los compiladores de Java podrán descubrir que x no desempeña ningún papel en la ejecución del código, por lo que lo eliminan por completo gracias a la optimización de la compilación.

Incluso si usas:

int x = 999;

x no debería incluirse: algunos compiladores tontos pueden incluirlo, pero esto podría cambiar en el futuro.

Sin embargo,

int x = methodA();

Definitivamente tendrá x incluido.

Los compiladores modernos eliminarán una gran cantidad de ineficiencia en el código, para que pueda concentrarse en sus problemas.