Lo que necesitas saber primero
1. Los programadores de C/C++ administran la memoria por sí mismos, mientras que el GC recupera automáticamente la memoria de Java.
Aunque no estoy muy familiarizado con C++, probablemente no cometí un error de sentido común al respecto.
2. ¿Qué es una pérdida de memoria?
Una pérdida de memoria se refiere a la existencia de memoria en el sistema que no se puede reciclar, lo que a veces causa memoria insuficiente o fallas del sistema.
En C/C++, las pérdidas de memoria se producen cuando la memoria asignada no se libera.
3. Hay una pérdida de memoria en Java. Primero debemos admitir esto antes de continuar discutiéndolo. Aunque Java tiene pérdidas de memoria, básicamente no es necesario que te preocupes por ello, especialmente aquellos que no son exigentes con el código en sí.
Las pérdidas de memoria en Java ciertamente significan: hay objetos inútiles que el recolector de basura no puede reciclar.
E incluso si hay un problema de pérdida de memoria, es posible que no aparezca.
4. Todos los parámetros en Java se pasan por valor.
Básicamente no hay ninguna objeción a los tipos básicos, pero no podemos tener ninguna objeción a los tipos de referencia.
Pérdidas de memoria de Java
1. Desbordamiento de memoria del montón (outOfMemoryError: espacio del montón de Java)
En la especificación JVM, la memoria del montón se utiliza para generar matrices y instancias de objetos.
Si se subdivide, la memoria del montón también se puede dividir en la generación joven y la generación anterior. La generación joven incluye un área del Edén y dos áreas de sobrevivientes.
Cuando se genera un nuevo objeto, el proceso de aplicación de memoria es el siguiente:
a. La jvm primero intenta asignar la memoria requerida para el nuevo objeto en el área del Edén;
b. Si el tamaño de la memoria es suficiente, la aplicación finaliza; de lo contrario, el siguiente paso es;
c. JVM inicia youngGC e intenta liberar objetos inactivos en el área de Eden. Después del lanzamiento, si el espacio de Eden aún no es suficiente para colocar nuevos objetos, intenta colocar algunos de los objetos activos en Eden en el área de Survivor;
d. El área de Survivor se utiliza como área de intercambio intermedia entre Eden y Old. Cuando el área ANTIGUA tenga suficiente espacio, los objetos en el área de Survivor se moverán al área Old; de lo contrario, se retendrán en el área de Survivor.
e. Cuando no hay suficiente espacio en el área ANTIGUA, la JVM realizará una GC completa en el área ANTIGUA;
f Después de la GC completa, si las áreas Survivor y OLD aún no pueden almacenar algunos objetos copiados de Eden, lo que hace que la JVM no pueda crear un área de memoria para nuevos objetos en el área de Eden, aparecerá un "error de falta de memoria":
outOfMemoryError: espacio del montón de Java
2. Desbordamiento de memoria en el área de método (outOfMemoryError: espacio permanente)
En la especificación JVM, el área de métodos almacena principalmente información de clases, constantes, variables estáticas, etc.
Por lo tanto, si el programa carga demasiadas clases o utiliza tecnología de generación de proxy dinámico como reflexión o gclib, puede causar un desbordamiento de memoria en esta área. Generalmente, el mensaje de error cuando ocurre un desbordamiento de memoria en esta área es:
outOfMemoryError: espacio permanente
3. Desbordamiento de la pila de subprocesos (java.lang.StackOverflowError)
La pila de subprocesos es una estructura de memoria exclusiva del subproceso, por lo que los problemas con la pila de subprocesos deben ser errores generados cuando un subproceso se está ejecutando.
Generalmente, el desbordamiento de la pila de subprocesos se debe a una recursividad demasiado profunda o demasiados niveles de llamadas a métodos.
El mensaje de error cuando se produce un desbordamiento de pila es:
Java. idioma. Error de desbordamiento de pila
Varios escenarios de pérdidas de memoria:
1. Los objetos de larga duración contienen referencias a objetos de corta duración.
Este es el escenario más común de pérdida de memoria y un problema común en el diseño de código.
Por ejemplo: si las variables locales se almacenan en caché en un mapa estático global y no se realiza una operación de limpieza, el mapa se hará cada vez más grande con el tiempo, lo que provocará pérdidas de memoria.
2. Modifique el valor del parámetro del objeto en hashset, y el parámetro es el campo utilizado para calcular el valor hash.
Después de que un objeto se almacena en la colección HashSet, los campos del objeto que participan en el cálculo del valor hash no se pueden modificar; de lo contrario, el valor hash modificado del objeto será diferente del valor hash cuando se almacenó originalmente en la colección HashSet. , en este caso, incluso si el método contiene usa la referencia actual del objeto como parámetro para recuperar el objeto de la colección HashSet, devolverá el resultado de que no se puede encontrar el objeto, lo que también resultará en una falla. elimina el objeto actual de la colección HashSet, lo que provoca una pérdida de memoria.
3. Establecer el número de conexiones y el tiempo de apagado de la máquina.
Abrir una conexión que consume muchos recursos durante mucho tiempo también puede provocar pérdidas de memoria.
Veamos un ejemplo de pérdida de memoria:
pila de clase pública {objeto privado [] elementos = nuevo objeto [10]; tamaño int privado = 0; objeto público pop () {si ( tamaño == 0) throw new EmptyStackException(); devolver elementos[--size]; } private void asegurarCapacity(){ if(elements.length == tamaño){ Objeto[] oldElements = elementos; elementos = nuevo Objeto[2 * elementos. longitud+1]; Sistema. arraycopy(oldElements,0, elementos, 0, tamaño);
El principio anterior debería ser muy simple. Si se agregan 10 elementos a la pila y luego se sacan todos, aunque la pila está vacía y no queremos nada, este objeto no se puede reciclar. Estado: Inútil, no se puede reciclar.
Pero incluso la existencia de tal cosa no necesariamente tendrá consecuencias si esta pila se usa menos.
Es solo un desperdicio de unos pocos K de memoria. De todos modos, nuestra memoria ya está hasta G, entonces, ¿qué impacto tendrá? Además, esto se reciclará pronto, entonces, ¿qué importa? Veamos dos ejemplos a continuación.
Ejemplo 1
clase pública Malo{ pila estática pública s=Pila(); empujar(nuevo objeto()); s. pop(); //Aquí hay una pérdida de memoria en un objeto. push(new Object()); //El objeto anterior se puede reciclar, lo que equivale a la autocuración}}
Debido a que es estático, existirá hasta que el programa salga, pero también podemos ver que tiene una función de autorreparación.
Es decir, si su pila tiene como máximo 100 objetos, entonces solo 100 objetos como máximo no se pueden reciclar. De hecho, esto debería ser fácil de entender. La pila contiene 100 referencias internas. El peor de los casos es que todas son inútiles. , porque una vez que pongamos nuevos avances, ¡las referencias anteriores naturalmente desaparecerán!
Ejemplo 2
clase pública NotTooBad{ public void hacerAlgo(){ Pila s=nueva Pila(); push(nuevo Objeto()); //otro código s. pop();//Esto también da como resultado que el objeto no se pueda reciclar y se pierda memoria. }// Salga del método, s no es válido automáticamente, s se puede reciclar y las referencias dentro de la pila desaparecen naturalmente, por lo que // También se puede realizar la autorreparación aquí, y se puede decir que este método no tiene Un problema de pérdida de memoria, pero se entregará más tarde // Solo se entrega a GC porque está cerrado y no está abierto al mundo exterior. Puedes decir lo anterior. Código 99. El 9999% de // situaciones no tendrá ningún impacto. Por supuesto, si escribe dicho código, no tendrá ningún efecto negativo, pero // definitivamente se puede decir que es código basura. ¡No hay contradicción! Agregaré uno. Un bucle for vacío no tendrá ningún gran impacto, ¿verdad?
Los dos ejemplos anteriores son simplemente triviales, pero las pérdidas de memoria en C/C++ no son malas, sino peores.
Si no se reciclan en un lugar, nunca podrán reciclarse. Si llama a este método con frecuencia, ¡la memoria se agotará!
Debido a que Java también tiene una función de autocuración (la nombré yo mismo y aún no he solicitado una patente), el problema de pérdida de memoria de Java puede casi ignorarse, pero aquellos que lo conocen no deberían cometerlo.
Para evitar pérdidas de memoria, puede consultar las siguientes sugerencias al escribir código:
1. Publicar referencias a objetos inútiles lo antes posible;
2. Utilice el procesamiento de cadenas, evite el uso de String y utilice StringBuffer ampliamente. Cada objeto String debe ocupar un área de memoria independiente;
3. Utilice la menor cantidad de variables estáticas posible, porque las variables estáticas se almacenan en la generación permanente (área de método) y la generación permanente básicamente no participa en la recolección de basura;
4. Evite crear objetos en bucles;
5. Abrir archivos grandes o tomar demasiados datos de la base de datos a la vez puede causar fácilmente un desbordamiento de la memoria, por lo que en estos lugares, debe calcular aproximadamente la cantidad máxima de datos y establecer los valores de espacio de memoria mínimo y máximo requeridos.