Asignaci贸n de memoria JVM

Hola a todos! Queremos coincidir la traducci贸n del material de hoy con el lanzamiento de un nuevo hilo en el curso Java Developer , que comienza ma帽ana. Bueno, empecemos.

JVM puede ser una bestia compleja. Afortunadamente, la mayor parte de esta complejidad est谩 oculta bajo el cap贸, y nosotros, como desarrolladores de aplicaciones y responsables de la implementaci贸n, a menudo no tenemos que preocuparnos mucho por eso. Aunque debido a la creciente popularidad de la tecnolog铆a para implementar aplicaciones en contenedores, vale la pena prestar atenci贸n a la asignaci贸n de memoria en la JVM.



Dos tipos de memoria

La JVM divide la memoria en dos categor铆as principales: heap y no heap. Un mont贸n es una pieza de memoria JVM con la que los desarrolladores est谩n m谩s familiarizados. Los objetos creados por la aplicaci贸n se almacenan aqu铆. Permanecen all铆 hasta que el recolector de basura los retire. Por lo general, el tama帽o de almacenamiento din谩mico que usa la aplicaci贸n var铆a seg煤n la carga actual.

La memoria fuera del mont贸n se divide en varias 谩reas. En HotSpot, puede usar el mecanismo de seguimiento de memoria nativa (NMT) para explorar las 谩reas de esta memoria. Tenga en cuenta que aunque NMT no rastrea el uso de toda la memoria nativa ( por ejemplo, no rastrea la asignaci贸n de memoria nativa por c贸digo de terceros ), sus capacidades son suficientes para la mayor铆a de las aplicaciones t铆picas de Spring. Para usar NMT, ejecute la aplicaci贸n con la -XX:NativeMemoryTracking=summary y usando jcmd VM.native_memory summary ver informaci贸n sobre la memoria utilizada.

Veamos el uso de NMT como ejemplo de nuestro viejo amigo Petclinic . El siguiente diagrama muestra el uso de la memoria JVM de acuerdo con los datos de NMT (menos su propia sobrecarga de NMT) al iniciar Petclinic con un tama帽o de -Xmx48M din谩mico m谩ximo de 48 MB ( -Xmx48M ):



Como puede ver, la memoria fuera del mont贸n representa la mayor parte de la memoria JVM utilizada, y la memoria del mont贸n es solo una sexta parte del total. En este caso, son aproximadamente 44 MB (de los cuales 33 MB se usaron inmediatamente despu茅s de la recolecci贸n de basura). El uso de memoria fuera del mont贸n totaliz贸 223 MB.

脕reas de memoria nativa

Espacio de clase comprimido : se utiliza para almacenar informaci贸n sobre clases cargadas. Limitado al par谩metro MaxMetaspaceSize . Una funci贸n del n煤mero de clases que se han cargado.

Nota del traductor

Por alguna raz贸n, el autor escribe sobre el espacio de clase comprimido, y no sobre el 谩rea de clase completa. El 谩rea de espacio de clase comprimido es parte del 谩rea de clase, y el par谩metro MaxMetaspaceSize limita el tama帽o del 谩rea de clase completa, no solo el espacio de clase comprimido. Para limitar el "espacio de clase comprimido", se utiliza el par谩metro CompressedClassSpaceSize .

Desde aqu铆 :
Si UseCompressedOops est谩 activado y se usa UseCompressedClassesPointers , entonces se usan dos 谩reas l贸gicamente diferentes de memoria nativa para los metadatos de la clase ...
Se asigna una regi贸n para estos punteros de clase comprimidos (los desplazamientos de 32 bits). El tama帽o de la regi贸n se puede establecer con CompressedClassSpaceSize y es de 1 gigabyte (GB) de forma predeterminada ...
MaxMetaspaceSize aplica a la suma del espacio de clase comprimido comprometido y el espacio para los metadatos de la otra clase

Si el par谩metro UseCompressedOops est谩 UseCompressedOops y UseCompressedOops usa UseCompressedOops , entonces UseCompressedOops usan dos 谩reas l贸gicamente diferentes de memoria nativa para los metadatos de la clase ...

Para punteros comprimidos, se asigna un 谩rea de memoria (compensaciones de 32 bits). CompressedClassSpaceSize puede establecer el tama帽o de esta 谩rea y, de forma predeterminada, es de 1 GB ...
El par谩metro MaxMetaspaceSize se refiere a la suma del 谩rea del puntero comprimido y el 谩rea para otros metadatos de clase.


  • Subproceso: la memoria utilizada por subprocesos en la JVM. La funci贸n del n煤mero de subprocesos en ejecuci贸n.
  • Cach茅 de c贸digo: la memoria utilizada por JIT para ejecutarlo. Una funci贸n del n煤mero de clases que se han cargado. Limitado a ReservedCodeCacheSize . Puede reducir la configuraci贸n de JIT, por ejemplo, deshabilitando la compilaci贸n escalonada.
  • GC (recolector de basura): almacena los datos utilizados por el recolector de basura. Depende del recolector de basura utilizado.
  • S铆mbolo: almacena caracteres como nombres de campo, firmas de m茅todos y cadenas internados. El uso excesivo de la memoria de caracteres puede indicar que las l铆neas est谩n demasiado internados.
  • Interno: almacena otros datos internos que no est谩n incluidos en ninguna de las otras 谩reas.

Las diferencias

En comparaci贸n con un mont贸n, la memoria fuera del mont贸n cambia menos bajo carga. Una vez que la aplicaci贸n haya cargado todas las clases que se utilizar谩n y el JIT est茅 completamente calentado, todo pasar谩 a un estado estable. Para ver una disminuci贸n en el uso del espacio de clases comprimido , el recolector de basura debe eliminar el cargador de clases que carg贸 las clases. Esto era com煤n en el pasado cuando las aplicaciones se implementaban en contenedores de servlets o servidores de aplicaciones (el recolector de basura elimin贸 el cargador de clases de aplicaciones cuando la aplicaci贸n se elimin贸 del servidor de aplicaciones), pero esto rara vez ocurre con los enfoques modernos para la implementaci贸n de aplicaciones.

Configurar JVM

Configurar la JVM para usar eficientemente la RAM disponible no es f谩cil. Si ejecuta la JVM con el par谩metro -Xmx16M y no espera -Xmx16M m谩s de 16 MB de memoria, obtendr谩 una sorpresa desagradable.

Un 谩rea interesante de la memoria JVM es el cach茅 de c贸digo JIT. Por defecto, HotSpot JVM usar谩 hasta 240 MB. Si el cach茅 de c贸digo es demasiado peque帽o, el JIT puede no tener suficiente espacio para almacenar sus datos y, como resultado, se reducir谩 el rendimiento. Si el cach茅 es demasiado grande, entonces la memoria puede desperdiciarse. Al determinar el tama帽o de un cach茅, es importante tener en cuenta su efecto tanto en el uso de la memoria como en el rendimiento.

Cuando se ejecuta en un contenedor Docker, las 煤ltimas versiones de Java ahora son conscientes de las limitaciones de memoria del contenedor y est谩n tratando de cambiar el tama帽o de la memoria JVM en consecuencia. Desafortunadamente, a menudo se asigna mucha memoria fuera del mont贸n y no hay suficiente en el mont贸n. Supongamos que tiene una aplicaci贸n ejecut谩ndose en un contenedor con 2 procesadores y 512 MB de memoria disponible. Desea manejar m谩s carga de trabajo y aumentar el n煤mero de procesadores a 4 y la memoria a 1 GB. Como discutimos anteriormente, el tama帽o del almacenamiento din谩mico generalmente var铆a con la carga, y la memoria fuera del almacenamiento din谩mico cambia significativamente menos. Por lo tanto, esperamos que la mayor铆a de los 512 MB adicionales se asignen al mont贸n para manejar el aumento de carga. Desafortunadamente, por defecto, la JVM no har谩 esto y distribuir谩 memoria adicional de manera m谩s o menos uniforme entre la memoria en el mont贸n y fuera del mont贸n.

Afortunadamente, el equipo de CloudFoundry tiene un amplio conocimiento de la asignaci贸n de memoria en la JVM. Si descarga aplicaciones a CloudFoundry, el paquete de compilaci贸n le aplicar谩 autom谩ticamente este conocimiento. Si no est谩 utilizando CloudFoudry o desea obtener m谩s informaci贸n sobre c贸mo configurar JVM, se recomienda leer la descripci贸n de la tercera versi贸n de la calculadora de memoria de Java buildpack .

驴Qu茅 significa esto para la primavera?

El equipo de Spring pasa mucho tiempo pensando en el rendimiento y el uso de la memoria, considerando la posibilidad de usar la memoria tanto en el mont贸n como fuera del mismo. Una forma de limitar el uso de memoria fuera del mont贸n es hacer que las partes del marco sean lo m谩s vers谩tiles posible. Un ejemplo de esto es usar Reflection para crear e inyectar dependencias en los beans de su aplicaci贸n. Mediante el uso de Reflection, la cantidad de c贸digo marco que usa permanece constante, independientemente de la cantidad de beans en su aplicaci贸n. Para optimizar el tiempo de inicio, utilizamos el cach茅 en el mont贸n, borrando este cach茅 despu茅s de que se complete el inicio. El recolector de basura puede limpiar f谩cilmente la memoria del mont贸n para proporcionar m谩s memoria disponible para su aplicaci贸n.

Tradicionalmente, esperamos sus comentarios sobre el material.

Source: https://habr.com/ru/post/445312/


All Articles