Conocimiento de gestión de memoria Java

  1. Estrategia de asignación de memoria

Desde la perspectiva de los principios de compilación, existen tres estrategias para la asignación de memoria durante la ejecución del programa, que son estáticas, de pila y de montón.

La asignación de almacenamiento estático significa que los requisitos de espacio de almacenamiento de cada objetivo de datos en tiempo de ejecución se pueden determinar en tiempo de compilación, por lo que se les puede asignar espacio de memoria fijo en tiempo de compilación. Esta estrategia de asignación requiere que las estructuras de datos variables (como las matrices variables) no estén permitidas en el código del programa, y ​​las estructuras anidadas o recursivas no están permitidas, ya que harán que el compilador no pueda calcular los requisitos de espacio de almacenamiento precisos.

La asignación de almacenamiento apilado también se puede llamar asignación de almacenamiento dinámico, que se implementa mediante una pila en ejecución similar a la pila. Contrariamente a la asignación de almacenamiento estático, en un esquema de almacenamiento de pila, los requisitos del programa para el área de datos son completamente desconocidos en el momento de la compilación, y solo se pueden conocer en tiempo de ejecución, pero se estipula que al ingresar un módulo de programa durante la operación, debe conocer Solo el tamaño del área de datos requerida por el módulo del programa puede asignarle memoria. Al igual que la pila con la que estamos familiarizados en las estructuras de datos, la asignación de almacenamiento de la pila se basa en el principio de la primera salida avanzada.

La asignación de almacenamiento estático requiere que conozca los requisitos de almacenamiento de todas las variables en tiempo de compilación, la asignación de almacenamiento de pila requiere que conozca todos los requisitos de almacenamiento en la entrada del proceso, y la asignación de almacenamiento de almacenamiento dinámico es específicamente responsable de la entrada del módulo en tiempo de compilación o tiempo de ejecución No se puede determinar la asignación de memoria de las estructuras de datos necesarias para el almacenamiento, como las cadenas de longitud variable y las instancias de objetos. El montón consta de grandes bloques de bloques disponibles o libres, y la memoria en el montón se puede asignar y liberar en cualquier orden.

  1. Apilar y apilar en la JVM

La JVM es una máquina virtual basada en pila. La JVM asigna una pila para cada subproceso recién creado, es decir, para un programa Java, su operación se completa a través de la operación de la pila. La pila guarda el estado del hilo en unidades de cuadros. La JVM solo realiza dos operaciones en la pila: operaciones push y pop en unidades de cuadros.

Java divide la memoria en dos tipos: uno es memoria de pila y el otro es memoria de montón

La pila y el montón son utilizados por Java para almacenar datos en Ram. A diferencia de C ++, Java gestiona automáticamente la pila y el montón, y el programador no puede configurar directamente la pila o el montón.

Pila: es una estructura de datos avanzada, generalmente utilizada para guardar parámetros y variables locales en métodos (funciones).

Montón (montón): es un espacio de memoria disponible dinámicamente (el sistema operativo mantiene la lista vinculada de espacio libre de memoria), es un área de datos de tiempo de ejecución, el espacio de memoria generado por la instrucción malloc en C está en el montón.

  1. Comparación de ventajas y desventajas del montón y la pila

La ventaja de la pila es que la velocidad de acceso es más rápida que la del montón, solo superada por los registros ubicados directamente en la CPU. Pero la desventaja es que el tamaño y la vida útil de los datos en la pila deben ser fijos y no tener flexibilidad. Además, los datos de la pila se pueden compartir, consulte el punto 3 para más detalles.

La ventaja del montón es que el tamaño de la memoria se puede asignar dinámicamente y la vida útil no tiene que avisar al compilador por adelantado. El recolector de basura de Java recopilará automáticamente los datos que ya no están en uso. Pero la desventaja es que debido a la asignación dinámica de memoria en tiempo de ejecución, la velocidad de acceso es lenta.

  1. Hay dos tipos de datos en Java

Uno es el tipo básico

Hay 8 tipos, a saber, int, short, long, byte, float, double, boolean, char (tenga en cuenta que no hay un tipo básico de cadena).

Este tipo de definición se define en una forma como int a = 3; long b = 255L; y se llama variable automática. Vale la pena señalar que las variables automáticas almacenan valores literales, no instancias de clases, es decir, no referencias de clase, y no hay clase aquí. Como int a = 3; donde a es una referencia para escribir int, apuntando al valor literal de 3. Los datos de estos valores literales se pueden conocer debido a su tamaño y duración (estos valores literales se definen de forma fija en un bloque de programa y el valor del campo desaparece después de que el bloque de programa sale). Por razones de velocidad, existen en la pila .

Además, la pila tiene una particularidad importante, es decir, los datos almacenados en la pila se pueden compartir. Supongamos que también definimos:

int a = 3;
int b = 3; el
compilador primero maneja int a = 3; primero creará una referencia a la variable a en la pila, luego encuentre si hay una dirección con un valor literal de 3, si no se encuentra, abra un Almacene la dirección del literal 3 y luego apunte a la dirección de 3. Luego, procese int b = 3; después de crear la variable de referencia de b, dado que ya hay un valor literal de 3 en la pila, apunte b directamente a la dirección de 3. De esta manera, hay una situación en la que a y b apuntan a 3 al mismo tiempo.

En particular, la referencia a este valor literal es diferente de la referencia al objeto de clase. Suponiendo que las referencias de dos objetos de clase apuntan a un objeto al mismo tiempo, si una variable de referencia de objeto modifica el estado interno del objeto, entonces la otra variable de referencia de objeto reflejará inmediatamente este cambio. Por el contrario, modificar el valor de una referencia literal no hace que cambie otra referencia a este valor literal. Como en el ejemplo anterior, después de haber definido los valores de ayb, dejemos que a = 4; entonces, b no será igual a 4 o igual a 3. Dentro del compilador, cuando encuentra a = 4; volverá a buscar si hay un valor literal de 4 en la pila, de lo contrario, volverá a abrir la dirección para almacenar el valor de 4; si ya existe, apunte directamente a a esta dirección . Por lo tanto, los cambios en el valor de a no afectarán el valor de b.

El otro son los datos de embalaje.

Tales como Integer, String, Double y otras clases que envuelven los tipos de datos básicos correspondientes. Todos estos tipos de datos existen en el montón. Java usa una nueva declaración () para decirle explícitamente al compilador que se crea dinámicamente en tiempo de ejecución según sea necesario, por lo que es más flexible, pero la desventaja es que lleva más tiempo.

La cadena es un tipo especial de datos de empaque. Se puede crear en forma de String str = new String ("abc"), o en forma de String str = "abc"; (en comparación, antes de JDK 5.0, nunca ha visto Integer i = 3; expresiones, porque las clases y los literales no son universales, excepto String. ¡En JDK 5.0, esta expresión es posible! Porque el compilador realiza la conversión Integer i = new Integer (3) en segundo plano) . El primero es un proceso de creación de clase estándar, es decir, en Java, todo es un objeto, y un objeto es una instancia de una clase, todo creado en forma de new (). Algunas clases en Java, como la clase DateFormat, pueden devolver una clase recién creada a través de su método getInstance (), que parece violar este principio. En realidad no. Esta clase usa el patrón singleton para devolver una instancia de la clase, pero esta instancia se crea dentro de la clase mediante new (), y getInstance () oculta este detalle desde el exterior. Entonces, ¿por qué no creó una instancia de new () en String str = "abc", ¿violó el principio anterior? En realidad no.

5) Almacenamiento de cadenas en memoria

La cadena es un tipo especial de datos de empaque que se puede crear de dos maneras:

String str = new String ("abc"); La primera forma de crear es usar new () para crear un nuevo objeto, se almacenará en el montón. Cada llamada crea un nuevo objeto.

String str = "abc"; El segundo método de creación es crear primero un str variable en la pila que se refiera al objeto de clase String, y luego encontrar en la pila si hay una dirección con el valor de "abc", si no, abrir un almacenamiento La dirección del valor literal es "abc", luego cree un nuevo objeto o de la clase String, apunte el valor de cadena de o a esta dirección y escriba el objeto referenciado o al lado de esta dirección en la pila. Si ya hay una dirección con el valor "abc", busque el objeto o, y devuelva la dirección de o, y finalmente apunte str a la dirección del objeto o.

Vale la pena señalar que los valores de cadena en la clase general de cadena se almacenan directamente. Pero al igual que String str = "abc", en este caso, el valor de la cadena es una referencia a los datos almacenados en la pila.

6. Almacenamiento de matriz en memoria

Cuando int x [] o int [] x, cree una referencia de matriz en el espacio de la pila de memoria y haga referencia a la matriz por el nombre de la matriz.

x = new int [5] asignará 5 espacios para almacenar datos int en la memoria del montón, la primera dirección de la memoria del montón se coloca en la memoria de la pila, y cada elemento de la matriz se inicializa a 0.

7. Almacenamiento de variables estáticas en la memoria.

Las variables y métodos modificados con static realmente especifican la "ubicación fija" de estas variables y métodos en el almacenamiento estático en memoria. Como hay una "posición fija", su "tamaño" parece ser fijo. Con las características de posición fija y tamaño fijo, es muy conveniente abrir espacio en la pila o en el montón. Si una variable o método estático no sale del alcance, su identificador de referencia no cambiará.

  1. Asignación de variables Java en memoria

1. Variables de clase (variables modificadas estáticamente)

Cuando se carga el programa, el sistema abre memoria para él en el montón y la dirección de memoria en el montón se almacena en la pila para acceso de alta velocidad. El ciclo de vida de las variables estáticas continúa hasta que se cierra todo el "sistema"

2. Variables de instancia

Cuando usa la palabra clave java new, el sistema se desarrolla en el montón y no necesariamente asigna espacio continuo a las variables (como las instancias de clase), y luego lo convierte en una larga cadena de números a través de un algoritmo hash basado en direcciones de memoria de montón dispersas Para caracterizar la "ubicación física" de esta variable en el montón. Ciclo de vida de la variable de instancia: cuando se pierde la referencia de la variable de instancia, el GC (recolector de basura) la incluirá en la "lista" reciclable, pero la memoria en el montón no se libera inmediatamente

3. Variables locales

Las variables locales se declaran en un método o un segmento de código (como un bucle for). Cuando se ejecuta, abre memoria en la pila. Cuando la variable local está fuera de alcance, la memoria se libera inmediatamente.

8 artículos originales publicados · Me gusta2 · Visitas 496

Supongo que te gusta

Origin blog.csdn.net/qq_42003546/article/details/102471459
Recomendado
Clasificación