instancia - tipos de variables en java
¿Las variables de instancia primitivas no inicializadas usan memoria? (6)
En Java, ¿le cuesta a la memoria declarar una variable de instancia de nivel de clase sin inicializarla?
Por ejemplo: Does int i;
use cualquier memoria si no la inicializo con i = 5;
?
Detalles:
Tengo una gran superclase que amplía muchas subclases diferentes (no lo suficientemente diferentes como para tener sus propias superclases). Algunas subclases no usan todas las primitivas declaradas por la superclase. ¿Puedo simplemente mantener primitivas como no inicializadas y solo inicializarlas en las subclases necesarias para ahorrar memoria?
En Java, cuando declaras un atributo de clase como String str;
, está declarando una referencia a un objeto, pero todavía no apunta a ningún objeto a menos que le afecte un valor str=value;
. Pero como puede adivinar, la referencia, incluso sin señalar un lugar de memoria, consume algo de memoria.
La pregunta original se refiere a las variables de nivel de clase y la respuesta es que sí usan espacio, pero también es interesante observar las del alcance del método.
Tomemos un pequeño ejemplo:
public class MemTest {
public void doSomething() {
long i = 0; // Line 3
if(System.currentTimeMillis() > 0) {
i = System.currentTimeMillis();
System.out.println(i);
}
System.out.println(i);
}
}
Si miramos el bytecode generado:
L0
LINENUMBER 3 L0
LCONST_0
LSTORE 1
Ok, como era de esperar, asignamos un valor en la línea 3 en el código, ahora si cambiamos la línea 3 a (y eliminamos la segunda impresión debido a un error del compilador):
long i; // Line 3
... y verifique el bytecode, entonces no se genera nada para la línea 3. Entonces, la respuesta es que no se usa memoria en este punto. De hecho, la LSTORE ocurre solo en la línea 5 cuando asignamos a la variable. Por lo tanto, declarar una variable de método no asignada no usa memoria y, de hecho, no genera ningún bytecode. Es equivalente a hacer la declaración donde primero se le asigna.
Ni la especificación de idioma de Java ni la especificación de máquina virtual de Java especifican la respuesta a esto porque es un detalle de implementación. De hecho, JVMS §2.7 dice específicamente :
Representación de objetos
Java Virtual Machine no exige ninguna estructura interna particular para objetos.
En teoría, una máquina virtual conforme podría implementar objetos que tienen muchos campos que usan un conjunto de indicadores de bits para marcar qué campos se han establecido en valores no predeterminados. Inicialmente no se asignarían campos, los bits de la bandera serían todos 0 y el objeto sería pequeño. Cuando se establece un campo por primera vez, el bit correspondiente de la bandera se establecerá en 1 y el tamaño del objeto se redimensionará para darle espacio. [El recolector de basura ya proporciona la maquinaria necesaria para pausar momentáneamente el código de ejecución con el fin de reubicar objetos vivos alrededor del montón, lo que sería necesario para redimensionarlos].
En la práctica, esto no es una buena idea porque, incluso si ahorra memoria, es complicado y lento. El acceso a los campos requeriría el bloqueo temporal del objeto para evitar la corrupción debido al multihilo; luego leyendo los bits de la bandera actual; y si el campo existe, entonces contar los bits establecidos para calcular el desplazamiento actual del campo deseado relativo a la base del objeto; luego leyendo el campo; y finalmente desbloqueando el objeto.
Por lo tanto, ninguna máquina virtual Java de propósito general hace algo como esto. Algunos objetos con un número exorbitante de campos podrían beneficiarse de él, pero incluso ellos no podrían confiar en él, ya que podrían necesitar ejecutarse en las máquinas virtuales comunes que no lo hacen.
Un diseño plano que asigna espacio para todos los campos cuando se crea una instancia de un objeto por primera vez es simple y rápido, por lo que ese es el estándar. Los programadores asumen que los objetos se asignan de esa manera y, por lo tanto, diseñan sus programas en consecuencia para aprovecharlo al máximo. Del mismo modo, los diseñadores de máquinas virtuales optimizan para hacer que ese uso sea rápido.
En definitiva, la disposición plana de los campos es una convención, no una regla, aunque de todos modos puedes confiar en ella.
Sí, la memoria asigna aunque no le asigne ningún valor.
int i;
Eso requiere 32 bit
memoria de 32 bit
(asignación). No importa si lo estás usando o no.
Algunas subclases no usan todas las primitivas declaradas por la superclase. ¿Puedo simplemente mantener primitivas como no inicializadas y solo inicializarlas en las subclases necesarias para ahorrar memoria?
De nuevo, no importa dónde se inicializó, la memoria asigna.
Lo único que debe tener cuidado es encontrar los primitivos no utilizados y eliminarlos.
Editar: Añadiendo un punto más que a diferencia de las referencias de la primitiva por valor predeterminado es null
, que lleva un recuerdo de
4 bytes(32-bit)
8 bytes on (64-bit)
Sí. En su clase, las variables asignarán su valor predeterminado incluso si no las inicializa.
En este caso, las variables int
se asignarán a 0
y ocuparán 4 bytes
por cada una.
Todos los miembros definidos en sus clases tienen valores predeterminados, incluso si no los inicializa explícitamente, por lo que sí usan memoria.
Por ejemplo, cada int
se inicializará por defecto a 0
y ocupará 4
bytes.
Para los miembros de la clase:
int i;
es lo mismo que :
int i = 0;
Esto es lo que dice el JLS sobre las variables de instancia:
Si una clase T tiene un campo a que es una variable de instancia, entonces una nueva variable de instancia a se crea y se inicializa a un valor predeterminado (§4.12.5) como parte de cada objeto recién creado de clase T o de cualquier clase que sea una subclase de T (§8.1.4). La variable de instancia efectivamente deja de existir cuando el objeto del cual es un campo ya no se referencia, después de que se haya completado cualquier finalización necesaria del objeto (§12.6).