sistema - simulador memoria java
¿Cuáles son algunas de las mejores prácticas de administración de memoria de Java? (13)
Estoy tomando algunas aplicaciones de un desarrollador anterior. Cuando ejecuto las aplicaciones a través de Eclipse, veo que el uso de la memoria y el tamaño del almacenamiento dinámico aumentan mucho. Tras una investigación más a fondo, veo que estaban creando un objeto una y otra vez en un bucle, así como otras cosas.
Empecé a revisar y limpiar. Pero cuanto más pasaba, más preguntas tenía "¿Esto realmente hará algo?"
Por ejemplo, en lugar de declarar una variable fuera del ciclo mencionado anteriormente y simplemente establecer su valor en el ciclo ... crearon el objeto en el ciclo. Lo que quiero decir es:
for(int i=0; i < arrayOfStuff.size(); i++) {
String something = (String) arrayOfStuff.get(i);
...
}
versus
String something = null;
for(int i=0; i < arrayOfStuff.size(); i++) {
something = (String) arrayOfStuff.get(i);
}
¿Soy incorrecto al decir que el ciclo inferior es mejor? Quizás estoy equivocado.
Además, ¿qué pasa después del segundo ciclo anterior, establezco "algo" de nuevo a nulo? ¿Eso borrará algo de memoria?
En cualquier caso, ¿cuáles son algunas buenas prácticas de administración de memoria que podría seguir y que ayudarán a mantener mi uso de memoria bajo en mis aplicaciones?
Actualizar:
Agradezco los comentarios de todos hasta ahora. Sin embargo, realmente no estaba preguntando acerca de los bucles anteriores (aunque según su consejo, volví al primer bucle). Estoy tratando de obtener algunas de las mejores prácticas que puedo vigilar. Algo en la línea de "cuando hayas terminado de usar una colección, despejala". Realmente necesito asegurarme de que estas aplicaciones no ocupen tanta memoria.
"¿Soy incorrecto al decir que el ciclo inferior es mejor?", La respuesta es NO, no solo es mejor, en el mismo caso es necesario ... La definición de la variable (no el contenido), se hace en el montón de Memoria, y limitado, en el primer ejemplo, cada bucle crea una instancia en esta memoria, y si el tamaño de "arrayOfStuff" es grande, puede ocurrir "Error de memoria insuficiente: espacio de almacenamiento en java" ....
Bueno, el primer ciclo es en realidad mejor, porque el alcance de algo es más pequeño. En cuanto a la gestión de la memoria, no supone una gran diferencia.
La mayoría de los problemas de memoria de Java se producen cuando almacena objetos en una colección, pero se olvida de eliminarlos. De lo contrario, el GC hace que su trabajo sea bastante bueno.
Como se indicó en el póster anterior, use Profiler para medir el uso de memoria (y / o CPU) de ciertas partes de su programa en lugar de intentar adivinarlo. ¡Quizás te sorprendas con lo que encuentres!
También hay un beneficio adicional para eso. Comprenderá más sobre su lenguaje de programación y su aplicación.
Uso VisualVM para crear perfiles y lo recomiendo en gran medida. Viene con distribución jdk / jre.
El primer ciclo es mejor. Porque
- la variable algo será clara más rápido (teórico)
- el programa es mejor para leer.
Pero desde el punto de memoria esto es irrelevante.
Si tiene problemas de memoria, debe indicar dónde se consume.
El primer ejemplo está bien. No hay ninguna asignación de memoria allí, aparte de una asignación de variable de pila y desasignación cada vez que pasa el ciclo (muy barato y rápido).
La razón es que todo lo que se ''asigna'' es una referencia, que es una variable de pila de 4 bytes (en la mayoría de los sistemas de 32 bits de todos modos). Una variable de pila se ''asigna'' al agregar a una dirección de memoria que representa la parte superior de la pila, por lo que es muy rápido y económico.
Lo que debe tener cuidado es para bucles como:
for (int i = 0; i < some_large_num; i++)
{
String something = new String();
//do stuff with something
}
como eso está haciendo allocatiton de memoria.
En mi opinión, debes evitar micro-optimizaciones como estas. Cuestan muchos ciclos cerebrales, pero la mayoría de las veces tienen poco impacto.
Su aplicación probablemente tenga algunas estructuras de datos centrales. Esos son los que deberían estar preocupados. Por ejemplo, si los llena, predíquelos con una buena estimación del tamaño, para evitar el cambio de tamaño repetido de la estructura subyacente. Esto se aplica especialmente a StringBuffer
, ArrayList
, HashMap
y similares. Diseña bien tu acceso a esas estructuras, para que no tengas que copiar mucho.
Use los algoritmos apropiados para acceder a las estructuras de datos. En el nivel más bajo, como el ciclo que mencionaste, usa Iterator
s, o al menos evita llamar a .size()
todo el tiempo. (Sí, está pidiendo la lista cada vez que se trata de su tamaño, que la mayoría de las veces no cambia). Por cierto, a menudo he visto un error similar con Map
s. Las personas keySet()
sobre keySet()
y get
cada valor, en lugar de simplemente iterar sobre el entrySet()
en primer lugar. El administrador de memoria le agradecerá los ciclos de CPU adicionales.
Esos dos bucles son equivalentes, excepto por el alcance de something
; mira esta pregunta para más detalles.
¿Mejores prácticas generales? Umm, veamos: no almacene grandes cantidades de datos en variables estáticas a menos que tenga una buena razón. Elimine los objetos grandes de las colecciones cuando haya terminado con ellos. Y, oh, sí, "medir, no adivinar". Use un generador de perfiles para ver dónde se está asignando la memoria.
La JVM es la mejor para liberar objetos efímeros. Intente no asignar objetos que no necesita. Pero no puede optimizar el uso de la memoria hasta que comprenda su carga de trabajo, la duración del objeto y el tamaño de los objetos. Un perfilador puede decirte esto.
Finalmente, la cosa n. ° 1 que debe evitar: nunca use Finalizer. Los finalizadores interfieren con la recolección de basura, ya que el objeto no puede liberarse, sino que debe ponerse en cola para su finalización, lo que puede ocurrir o no. Lo mejor es nunca usar finalizadores.
En cuanto al uso de memoria que está viendo en Eclipse, no es necesariamente relevante. El GC hará su trabajo basado en la cantidad de memoria libre que hay. Si tiene mucha memoria libre, es posible que no vea un solo GC antes de que se cierre la aplicación. Si encuentra que su aplicación se está quedando sin memoria, solo un perfilador real puede indicarle dónde están las fugas o las ineficiencias.
Los dos bucles usarán básicamente la misma cantidad de memoria, cualquier diferencia sería insignificante. "Cadena de algo" solo crea una referencia a un objeto, no un objeto nuevo en sí mismo y, por lo tanto, cualquier memoria adicional utilizada es pequeña. Además, el compilador / combinado con JVM probablemente optimice el código generado de todos modos.
Para las prácticas de gestión de la memoria, realmente debe tratar de perfilar su memoria mejor para comprender dónde están realmente los cuellos de botella. Mire especialmente las referencias estáticas que apuntan a una gran parte de la memoria, ya que eso nunca se recopilará.
También puede consultar Referencias Débiles y otras clases especializadas de administración de memoria.
Por último, tenga en cuenta que si una aplicación ocupa memoria, puede haber una razón para ello ...
Actualización La clave para la administración de la memoria son las estructuras de datos, así como la cantidad de rendimiento que necesita / cuándo. La compensación suele ser entre la memoria y los ciclos de la CPU.
Por ejemplo, se puede ocupar una gran cantidad de memoria con el almacenamiento en caché, que está específicamente allí para mejorar el rendimiento ya que está tratando de evitar una operación costosa.
Así que piense a través de sus estructuras de datos y asegúrese de no guardar cosas en la memoria por más tiempo de lo necesario. Si se trata de una aplicación web, evite almacenar una gran cantidad de datos en la variable de sesión, evite tener referencias estáticas a grandes cantidades de memoria, etc.
No hay objetos creados en ambos ejemplos de código. Simplemente establece una referencia de objeto a una cadena que ya está en la matrizOfStuff. Entonces, en la memoria no hay diferencia.
No intentes superar al VM. El primer ciclo es la mejor práctica recomendada, tanto para el rendimiento como para la mantenibilidad. Volver a establecer la referencia en nulo después del ciclo no garantizará la liberación inmediata de la memoria. El GC hará su mejor trabajo cuando use el alcance mínimo posible.
Los libros que cubren estas cosas en detalle (desde la perspectiva del usuario) son Effective Java 2 and Implementation Patterns .
Si desea obtener más información sobre el rendimiento y los aspectos internos de la máquina virtual, necesita ver las charlas o leer libros de Brian Goetz .
Por lo que entiendo que estás viendo, el ciclo inferior no es mejor. La razón es que incluso si está intentando reutilizar una sola referencia (Ex-algo), el hecho es que el objeto (Ex-arrayOfStuff.get (i)) todavía se está haciendo referencia desde la Lista (arrayOfStuff). Para que los objetos sean elegibles para la recopilación, no se les debe hacer referencia desde ningún lugar. Si está seguro de la vida de la lista después de este punto, puede decidir eliminar / liberar los objetos de ella, dentro de un bucle separado.
La optimización que se puede hacer desde una perspectiva estática (es decir, no se está produciendo ninguna modificación en esta Lista desde ningún otro subproceso), es mejor evitar la invocación de tamaño () repetidamente. Es decir, si no espera que el tamaño cambie, entonces ¿por qué calcularlo una y otra vez? después de todo, no es un array.length, es list.size ().
Si aún no lo ha hecho, le sugiero que instale la plataforma Eclipse Test & Performance Tools (TPTP). Si desea volcar e inspeccionar el montón, consulte las herramientas jmap y jhat del SDK. Consulte también Supervisión y administración de las aplicaciones de la plataforma Java SE 6 .