2012-04-11 13 views
26

Todavía estoy aprendiendo las cuerdas de Java, lo siento mucho si hay una respuesta obvia a esto. Tengo un programa que me está costando mucha memoria y quiero encontrar una forma de reducir su uso, pero después de leer muchas preguntas de SO, tengo la idea de que necesito demostrar dónde está el problema antes de comenzar a optimizarlo.¿Cómo perfil de memoria en Java?

Así que esto es lo que hice, agregué un punto de interrupción al inicio de mi programa y lo ejecuté, luego comencé visualVM y tuve el perfil de la memoria (también hice lo mismo en netbeans solo para comparar los resultados y ellos son lo mismo). Mi problema es que no sé cómo leerlos, obtuve el área más alta simplemente diciendo char[] y no puedo ver ningún código ni nada (lo cual tiene sentido porque visualvm se está conectando a jvm y no puede ver mi fuente, pero netbeans tampoco me muestra la fuente como lo hace cuando se hace el perfil de la CPU).

Básicamente lo que quiero saber es qué variable (y con suerte más detalles como en qué método) toda la memoria se está utilizando para que pueda concentrarse en trabajar allí. ¿Hay una manera fácil de hacer esto? En este momento estoy usando eclipse y java para desarrollar (e instalé visualVM y netbeans específicamente para perfilar pero estoy dispuesto a instalar cualquier otra cosa que creas que hace este trabajo).

EDITAR: Idealmente, estoy buscando algo que tome todos mis objetos y ordenarlos por tamaño (para que pueda ver cuál está acaparando la memoria). Actualmente devuelve información genérica como string [] o int [], pero quiero saber a qué objeto se refiere para que pueda optimizar su tamaño.

+0

Utilizo un generador de perfiles que me muestra dónde se asignan los objetos. No sé si VisualVM puede hacer esto, pero es muy útil. El que yo uso es YourKit, pero no es gratuito (pero puedes obtener una licencia de evaluación) –

+0

@PeterLawrey De hecho, leí una respuesta que escribiste hace un tiempo en la que mencionaste que tu kit era tu primera opción, lo busqué (ya que tu consejo siempre es increíble) pero sí es un poco caro y solo estoy aprendiendo. –

+0

sin usar algo que instrumente su código, ningún analizador podrá señalar qué instancia == lo que usted llamó una referencia en su fuente. –

Respuesta

22

Las cadenas son problemáticos

Básicamente en Java, String referencias (cosas que utilizan char[] detrás de las escenas) dominará la mayor parte negocio de la memoria aplicaciones sabia. La forma en que se crean determina la cantidad de memoria que consumen en la JVM.

Simplemente porque son tan fundamentales para la mayoría de las aplicaciones comerciales como tipo de datos, y también son uno de los que más hambre tienen en la memoria. Esto no es solo una cosa de Java, los tipos de datos String ocupan mucha memoria en casi todos los idiomas y en la biblioteca de tiempo de ejecución, porque al menos son solo matrices de 1 byte por carácter o en el peor (Unicode) son matrices. de múltiples bytes por personaje.

vez al perfilar uso de CPU en una aplicación web que también tenía una dependencia JDBC de Oracle descubrí que StringBuffer.append() dominaron a los ciclos de la CPU en muchos órdenes de magnitud con respecto a todas las demás llamadas a métodos combinado, mucho menos cualquier otra llamada al método individual. El controlador JDBC realizó muchas manipulaciones de String, una especie de compensación por el uso de PreparedStatements para todo.

Lo que usted está preocupado por que no se puede controlar, no directamente de todos modos

Lo que usted debe centrarse en lo que es en su control, que es asegurarse de que no se aferran a las referencias más largo que lo necesita, y que no está duplicando cosas innecesariamente. Las rutinas de recolección de basura en Java están altamente optimizadas, y si aprende cómo funcionan sus algoritmos, puede asegurarse de que su programa se comporte de la manera óptima para que esos algoritmos funcionen.

Java memoria de la pila no es como logró manualmente la memoria en otros idiomas, esas reglas no se aplican

Lo que se consideran pérdidas de memoria en otros idiomas no son la misma cosa/root como causa en Java con su sistema de recolección de basura.

Lo más probable es que en la memoria Java no se consuma un solo objeto uber que tenga fugas (referencia colgante en otros entornos).

lo más probable es un montón de asignaciones más pequeñas debido a StringBuffer/StringBuilder objetos no del tamaño adecuado en las primeras instantantations y luego tener que crecer de forma automática los char[] arrays para almacenar posteriores append() llamadas.

Estos objetos intermedios se pueden retener durante más tiempo de lo esperado por el recolector de basura debido al alcance que tienen y muchas otras cosas que pueden variar en el tiempo de ejecución.

Ejemplo: el recolector de basura puede decidir que no son candidatos, sino porque considera que hay un montón de memoria todavía no se tenía que podría ser el momento demasiado caro aconsejable eliminar a cabo en ese punto en el tiempo, y esperará hasta que la presión de la memoria aumente.

El recolector de basura es realmente bueno ahora, pero no es mágico, si estás haciendo cosas degeneradas, hará que no funcione de manera óptima. Hay una gran cantidad de documentación en Internet sobre la configuración del recolector de basura para todas las versiones de las JVM.

Estos objetos no referenciados pueden no haber llegado al tiempo que el recolector de basura cree que los necesita para borrarlos de la memoria, o podría haber referencias a ellos en algún otro objeto (List) por ejemplo que no te das cuenta aún apunta a ese objeto. Esto es lo que comúnmente se conoce como fuga en Java, que es una fuga de referencia más específicamente.

Ejemplo: Si usted sabe que necesita para construir una 4K String utilizando un StringBuilder crearlo con no new StringBuilder(4096); el valor por defecto, que es como 32 e inmediatamente se iniciará la creación de la basura que puede representar muchas veces lo que creo que el objeto debe ser tamaño sabio

Puede descubrir cuántos de los tipos de objetos se crean instancias con VisualVM, esto le dirá lo que necesita saber. No va a haber una gran luz intermitente que apunte a una sola instancia de una sola clase que diga: "¡Este es el gran consumidor de la memoria!", Es decir, a menos que haya una sola instancia de alguna char[] que esté leyendo alguna archivo masivo en, y esto tampoco es posible, porque muchas otras clases usan char[] internamente; y entonces ya casi lo sabías.

no veo ninguna mención de OutOfMemoryError

Es probable que no tiene un problema en su código, el sistema de recolección de basura simplemente podría no ser conseguir poner bajo presión suficiente para entran en juego y objetos desasignar que piensa que debería estar limpiando. Lo que cree que es un problema probablemente no lo es, a menos que su programa se bloquee con OutOfMemoryError.Esto no es C, C++, Objective-C o cualquier otro lenguaje de gestión de memoria manual/tiempo de ejecución. No puede decidir qué hay en la memoria o no en el nivel de detalle que espera que pueda.

0

Si utiliza visualVM para verificar el uso de la memoria, se centra en los datos, no en los métodos. Tal vez su gran char [] datos es causada por muchos valores de cadena? A menos que esté utilizando recursividad, los datos no serán de variables locales. Entonces puede enfocarse en los métodos que insertan elementos en estructuras de datos grandes. Para averiguar qué declaraciones precisas causan que su "pérdida de memoria", le sugiero que, además,

2

Recomendaría capturar montones de pilas y usar una herramienta como Eclipse MAT que le permita analizarlas. Hay muchos tutorials disponibles. Proporciona una vista del dominator tree para proporcionar información sobre las relaciones entre los objetos en el montón. Específicamente para lo que mencionaste, la característica "path to GC roots" de MAT te dirá dónde se hace referencia a la mayoría de esos objetos char [], String [] e int []. JVisualVM también puede ser útil para identificar fugas y asignaciones, en particular mediante el uso de instantáneas con rastros de pila de asignación. Hay bastantes walk-throughs of the process de obtener las instantáneas y compararlas para encontrar el punto de asignación.

7

En JProfiler, puede ir al montón y activar la vista más grande de los objetos. Verá los objetos que retienen más memoria. La memoria "retenida" es la memoria que liberaría el recolector de basura si eliminó el objeto.

A continuación, puede abrir los nodos de objeto para ver el árbol de referencia de los objetos retenidos. Aquí hay una captura de pantalla de la mayor objeto de vista:

enter image description here

responsabilidad: Mi empresa desarrolla JProfiler

1

JDK de Java viene con jvisualvm en la carpeta bin, una vez que el servidor de aplicaciones (por ejemplo, está en marcha) puede ejecutar VisualVM y conectarlo a su servidor local, que le proporcionará la asignación de memoria y le permiten realizar volcado del montón

enter image description here

Para obtener más información sobre cómo habilitar: http://sysdotoutdotprint.com/index.php/2017/08/01/turn-profiler-java/

Cuestiones relacionadas