visual remote jdk java profiling visualvm

java - remote - visualvm linux



¿Cómo memorizar el perfil en Java? (5)

Todavía estoy aprendiendo las cuerdas de Java, lo siento mucho si hay una respuesta obvia a esto. Tengo un programa que está tomando una gran cantidad de memoria y quiero encontrar una manera 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.

Así que esto es lo que hice, agregué un punto de ruptura al inicio de mi programa y lo ejecuté, luego comencé visualVM y puse mi perfil en la memoria (también hice lo mismo en netbeans solo para comparar los resultados y son los mismos ). 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 la jvm y no puedo ver mi fuente, pero Netbeans tampoco me muestra la fuente como lo hace al hacer 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) se está utilizando toda la memoria para poder concentrarme 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 crear perfiles, pero estoy dispuesto a instalar cualquier otra cosa que crea que haga este trabajo).

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


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

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

Descargo de responsabilidad: Mi empresa desarrolla JProfiler


Java JDK viene con JVisualVM en la carpeta bin, una vez que su servidor de aplicaciones (por ejemplo, se está ejecutando) puede ejecutar visualvm y conectarlo a su host local, que le proporcionará la asignación de memoria y le permitirá realizar un volcado de pila

Para obtener pasos más detallados sobre cómo habilitar: http://sysdotoutdotprint.com/technologies/java/6


Recomendaría capturar volcados de pila y utilizar una herramienta como Eclipse MAT que le permita analizarlos. Hay muchos tutorials disponibles. Proporciona una vista del árbol de dominadores para proporcionar información sobre las relaciones entre los objetos en el montón. Específicamente por lo que mencionó, la función "ruta de acceso a las raíces GC" de MAT le indicará dónde se hace referencia a la mayoría de los 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 trazas de pila de asignación. Hay bastantes recorridos del proceso para obtener las instantáneas y compararlas para encontrar el punto de asignación.


Si usa visualVM para verificar el uso de la memoria, se enfoca en los datos, no en los métodos. ¿Es posible que los datos de tu gran carácter [] estén causados ​​por muchos valores de cadena? A menos que esté utilizando la recursión, los datos no serán de variables locales. Por lo tanto, puede centrarse en los métodos que insertan elementos en grandes estructuras de datos. Para averiguar qué declaraciones precisas causan su "pérdida de memoria", le sugiero que adicionalmente


Las cuerdas son problematicas

Básicamente, en Java, las referencias de String (cosas que usan char[] detrás de la escena) dominarán la mayoría de las aplicaciones de memoria de aplicaciones empresariales . 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 de negocios como tipo de datos, y también son uno de los que más necesitan memoria. Esto no es solo una cosa de Java, los tipos de datos de String ocupan mucha memoria en casi todos los idiomas y en la biblioteca de tiempo de ejecución, porque al menos son solo arreglos de 1 byte por carácter o, en el peor de los casos (Unicode), son arreglos de múltiples bytes por carácter.

Una vez, al perfilar el uso de la CPU en una aplicación web que también tenía una dependencia de Oracle JDBC, descubrí que StringBuffer.append() dominaba los ciclos de la CPU en muchos órdenes de magnitud sobre todas las otras llamadas de métodos combinadas , y mucho menos cualquier otra llamada de método único. El controlador JDBC hizo montones y montones de manipulación de String , el tipo de intercambio de usar PreparedStatements para todo.

Lo que le preocupa no lo puede controlar, no directamente de todos modos

En lo que debería concentrarse es en lo que está bajo su control, que consiste en asegurarse de no mantener las referencias más tiempo del necesario y en que no está duplicando cosas innecesariamente. Las rutinas de recolección de basura en Java están altamente optimizadas, y si aprendes cómo funcionan sus algoritmos, puedes asegurarte de que tu programa se comporte de la manera óptima para que funcionen esos algoritmos.

Java Heap Memory no es como la memoria administrada manualmente en otros idiomas, esas reglas no se aplican

Lo que se considera pérdida de memoria en otros idiomas no es lo mismo / causa raíz que en Java con su sistema de recolección de basura.

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

Es muy probable que sea un montón de asignaciones más pequeñas debido a que los objetos StringBuffer / StringBuilder no tienen el tamaño adecuado en las primeras instantes y luego tienen que hacer crecer automáticamente las matrices char[] para retener las llamadas append() posteriores.

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

EJEMPLO: el recolector de basura puede decidir que hay candidatos, pero como se considera que todavía hay mucha memoria, podría ser demasiado costoso tiempo vaciarlos en ese momento, y esperará hasta que la memoria la presión aumenta

El recolector de basura es realmente bueno ahora, pero no es magia, si está 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.

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

EJEMPLO: si sabe que necesita construir una String 4K utilizando un StringBuilder con el new StringBuilder(4096); no es el valor predeterminado, que es como 32 e inmediatamente comenzará a crear basura que puede representar muchas veces lo que piensas que el objeto debería tener en cuanto al tamaño.

Puede descubrir cuántos de qué 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 memoria!", Es decir, a menos que solo haya una instancia de algún char[] que eres leer un archivo masivo en, y esto tampoco es posible, porque muchas otras clases usan char[] internamente; y entonces ya casi sabías eso.

No veo ninguna mención de OutOfMemoryError

Probablemente no tenga un problema en su código, es posible que el sistema de recolección de basura no esté siendo presionado lo suficiente como para poner en marcha y desasignar los objetos que cree que deberían estar limpiando. Lo que cree que es un problema probablemente no lo sea, no a menos que su programa se bloquee con OutOfMemoryError . Esto no es C, C ++, Objective-C, o cualquier otro lenguaje / tiempo de ejecución de administración de memoria manual. No puede decidir lo que está en la memoria o no en el nivel de detalle que espera que pueda.