Introducción a la caché de código de JVM

1. Introducción

En este tutorial, vamos a echar un vistazo rápido y aprender sobre la memoria caché de código de la JVM.

2. ¿Qué es la caché de código?

En pocas palabras, JVM Code Cache es un área donde JVM almacena su código de bytes compilado en código nativo . Llamamos a cada bloque del código nativo ejecutable un método . El nmethod puede ser un método Java completo o en línea.

El compilador Just-In-Time (JIT) es el mayor consumidor del área de caché de código. Es por eso que algunos desarrolladores llaman a esta memoria caché de código JIT.

3. Ajuste de caché de código

La caché de código tiene un tamaño fijo . Una vez que esté lleno, la JVM no compilará ningún código adicional ya que el compilador JIT ahora está apagado. Además, recibiremos el mensaje de advertencia "CodeCache está lleno ... El compilador ha sido deshabilitado ". Como resultado, terminaremos con un rendimiento degradado en nuestra aplicación. Para evitar esto, podemos ajustar la caché de código con las siguientes opciones de tamaño:

  • InitialCodeCacheSize : el tamaño de la caché del código inicial, 160 K predeterminado
  • ReservedCodeCacheSize : el tamaño máximo predeterminado es 48 MB
  • CodeCacheExpansionSize : el tamaño de expansión del caché de código, 32KB o 64KB

El aumento de ReservedCodeCacheSize puede ser una solución, pero normalmente es solo una solución temporal.

Afortunadamente, la JVM ofrece una opción UseCodeCacheFlushing para controlar el vaciado del área de caché de código . Su valor predeterminado es falso. Cuando lo habilitamos, libera el área ocupada cuando se cumplen las siguientes condiciones:

  • la caché de código está llena; esta área se enjuaga si su tamaño excede un cierto umbral
  • se pasa el intervalo determinado desde la última limpieza
  • el código precompilado no es lo suficientemente atractivo. Para cada método compilado, la JVM realiza un seguimiento de un contador de calor especial. Si el valor de este contador es menor que un umbral calculado, la JVM libera este fragmento de código precompilado

4. Uso de la caché de código

Para monitorear el uso de la caché de código, necesitamos rastrear el tamaño de la memoria actualmente en uso.

Para obtener información sobre el uso de la caché de código, podemos especificar la opción –XX: + PrintCodeCache JVM . Después de ejecutar nuestra aplicación, veremos un resultado similar:

CodeCache: size=32768Kb used=542Kb max_used=542Kb free=32226Kb 

Veamos qué significa cada uno de estos valores:

  • tamaño en la salida muestra el tamaño máximo de la memoria, que es idéntico a ReservedCodeCacheSize
  • utilizado es el tamaño real de la memoria que está actualmente en uso
  • max_used es el tamaño máximo que se ha utilizado
  • libre es la memoria restante que aún no está ocupada

La opción PrintCodeCache es muy útil, ya que podemos:

  • mira cuando ocurre el enrojecimiento
  • determinar si llegamos a un punto crítico de uso de memoria

5. Caché de código segmentado

A partir de Java 9, la JVM divide el caché de código en tres segmentos distintos, cada uno de los cuales contiene un tipo particular de código compilado . Para ser más específicos, hay tres segmentos:

  • El segmento sin método contiene código relacionado interno de JVM, como el intérprete de código de bytes. De forma predeterminada, este segmento es de alrededor de 5 MB. Además, es posible configurar el tamaño del segmento a través del indicador de ajuste -XX: NonNMethodCodeHeapSize
  • El segmento de código perfilado contiene código ligeramente optimizado con una vida útil potencialmente corta. Aunque el tamaño del segmento es de alrededor de 122 MB por defecto, podemos cambiarlo a través del indicador de ajuste -XX: ProfiledCodeHeapSize
  • El segmento sin perfil contiene código completamente optimizado con una vida útil potencialmente larga. Del mismo modo, es alrededor de 122 MB por defecto. Este valor es, por supuesto, configurable a través de la marca de ajuste -XX: NonProfiledCodeHeapSize

Esta nueva estructura trata a varios tipos de código cumplido de manera diferente, lo que conduce a un mejor rendimiento general.

Por ejemplo, separar el código compilado de corta duración del código de larga duración mejora el rendimiento del barrido de métodos, principalmente porque necesita escanear una región de memoria más pequeña.

6. Conclusión

Este artículo rápido presenta una breve introducción a la caché de código JVM.

Además, presentamos algunas opciones de uso y ajuste para monitorear y diagnosticar esta área de memoria.