
Application Class Data Sharing (AppCDS) : función JVM para acelerar el inicio y ahorrar memoria. Después de haber aparecido en su infancia en HotSpot en JDK 1.5 (2004), durante mucho tiempo permaneció muy limitado e incluso parcialmente comercial. Solo con OpenJDK 10 (2018) se puso a disposición de simples mortales, al mismo tiempo que se amplió el alcance. Y recientemente lanzado Java 13 intentó simplificar esta aplicación.
La idea de AppCDS es "compartir" clases una vez cargadas entre instancias de la misma JVM en el mismo host. Parece que esto debería ser excelente para los microservicios, especialmente los "broilers" en Spring Boot con sus miles de clases de biblioteca, porque ahora estas clases no necesitarán cargarse (analizarse y verificarse) en cada inicio de cada instancia de JVM, y no se duplicarán en la memoria. Esto significa que el lanzamiento debería ser más rápido y el consumo de memoria debería ser menor. Maravilloso, ¿no es así?
Todo es así, todo es así. Pero si usted, el odnokhabryanin, solía creer no en los letreros del bulevar, sino en números y ejemplos específicos, entonces bienvenido a kat, tratemos de descubrir cómo es realmente ...
En lugar de descargo de responsabilidad
Ante ti no es una guía para usar AppCDS, sino un resumen de los resultados de un pequeño estudio. Estaba interesado en comprender cómo esta función JVM es aplicable en mi proyecto de trabajo, y traté de evaluarla desde la perspectiva de un desarrollador empresarial, exponiendo el resultado en este artículo. Esto no incluyó temas como el uso de AppCDS en la ruta del módulo, la implementación de AppCDS en otras máquinas virtuales (no HotSpot) y las complejidades del uso de contenedores. Pero hay una parte teórica para explorar el tema, así como una parte experimental escrita para que pueda repetir la experiencia usted mismo. Ninguno de los resultados se ha aplicado aún en producción, pero quién sabe cómo será el mañana ...
Teoría
Una breve introducción a AppCDS
Es posible que se haya familiarizado con este tema en varias fuentes, por ejemplo:
- en un artículo de Nikolai Parlog (incluidos los bollos Java 13, pero sin Spring Boot)
- en un informe y artículo de Volker Simonis (sin Java 13, pero con detalles)
- en un informe del autor de estas líneas (sin Java 13, pero con énfasis en Spring Boot)
Para no volver a contar, destacaré solo algunos puntos que son importantes para este artículo.
En primer lugar, AppCDS es una extensión de la función CDS que ha aparecido durante mucho tiempo en HotSpot, cuya esencia es la siguiente:

Para hacer realidad ambas ideas, debe hacer lo siguiente (en términos generales):
- Obtenga una lista de clases que desea compartir entre instancias de aplicación
- Combinar estas clases en un archivo adecuado para la asignación de memoria
- Conecte el archivo a cada instancia de la aplicación al inicio
Parece que el algoritmo tiene solo 3 pasos: tómalo y hazlo. Pero aquí comienzan las noticias, todo tipo de cosas.
Lo malo es que, en el peor de los casos, cada uno de estos elementos se convierte en al menos un lanzamiento de JVM con sus propias opciones específicas, lo que significa que todo el algoritmo es un malabarismo sutil del mismo tipo de opciones y archivos. Eso no suena muy prometedor, ¿verdad?
Pero hay buenas noticias: el trabajo para mejorar este algoritmo está en curso , y con cada lanzamiento de Java, su aplicación se vuelve más fácil. Entonces, por ejemplo:
- En OpenJDK 10 y 11, puede omitir el paso 1 si desea compartir solo las clases principales de JDK, ya que ya han sido compiladas para nosotros y puestas en
$JAVA_HOME\lib\classlist
(≈1200 piezas). - En OpenJDK 12, puede omitir el paso 2 , porque junto con la lista de clases, el archivo de distribución también incluye un archivo listo para usar, que se usa de fábrica y no requiere una conexión explícita.
- En caso de que quieras compartir todo lo demás (y generalmente solo quieres)
OpenJDK 13 proporciona archivos dinámicos de CDS: archivos que se recopilan durante el funcionamiento de la aplicación y se guardan cuando tiene personal. Esto le permite contraer los puntos 1 y 2 en un punto no demasiado confuso (aunque no todo es tan simple, pero más sobre eso más adelante).
Por lo tanto, no importa cuál sea el proceso de preparación de AppCDS, los 3 pasos enumerados anteriormente siempre están detrás, solo en algunos casos están velados.
Como probablemente haya notado, con el advenimiento de AppCDS, muchas clases de aplicaciones comienzan una doble vida: viven simultáneamente en sus lugares anteriores (con mayor frecuencia archivos JAR) y en un nuevo archivo compartido. Al mismo tiempo, el desarrollador continúa cambiándolos / eliminándolos / completándolos en el mismo lugar, y la JVM los toma del nuevo cuando trabaja. Uno no necesita ser un adivino para ver el peligro de tal situación: si no se hace nada, tarde o temprano copias de las clases se corroerán, y obtendremos muchos encantos del típico "infierno JAR". Está claro que la JVM no puede evitar cambios de clase, pero debería ser capaz de detectar una discrepancia en el tiempo. Sin embargo, hacer esto comparando clases por pares, incluso por sumas de verificación, es una idea; puede negar el resto de las ganancias de productividad. Esta es probablemente la razón por la cual los ingenieros de JVM no seleccionaron las clases individuales como el objeto de comparación, sino el classpath completo, y declararon en la documentación de AppCDS: "El classpath al crear un archivo compartido debe ser el mismo (o al menos un prefijo) que con los lanzamientos de aplicaciones posteriores".
Tenga en cuenta que el classpath utilizado en el momento de creación del archivo debe ser el mismo (o un prefijo) que el classpath utilizado en el tiempo de ejecución.
Pero esta no es una declaración inequívoca, porque, como recordará, un classpath se puede formar de diferentes maneras, como:
- leer archivos
.class
desnudos de directorios de paquetes compilados,
Por ejemplo, java com.example.Main
- escanear directorios con archivos JAR cuando se usa comodín,
por ejemplo, java -cp mydir/* com.example.Main
- listado explícito de archivos JAR y / o ZIP,
por ejemplo, java -cp lib1.jar;lib2.jar com.example.Main
(esto sin mencionar el hecho de que classpath también se puede configurar de manera diferente, por ejemplo, a través de las opciones JVM -cp/-classpath/--class-path
, la variable de entorno CLASSPATH
o el atributo del archivo JAR de Class-Path
que se lanzará)
De estos métodos, solo uno es compatible con AppCDS: enumeración explícita de archivos JAR. Aparentemente, los ingenieros de HotSpot JVM consideraron que comparar classpaths en el archivo AppCDS y en la aplicación lanzada sería lo suficientemente rápido y confiable solo si se especificaran con la mayor claridad posible , con una lista exhaustiva habitual.
CDS / AppCDS solo admite clases de archivo de archivos JAR.
Es importante tener en cuenta aquí que esta declaración no es recursiva, es decir no se aplica a los archivos JAR dentro de los archivos JAR (a menos que se trate de CDS dinámico, consulte a continuación). Esto significa que las muñecas JAR habituales emitidas por Spring Boot no funcionarán así con AppCDS regular, tendrá que sentarse.
Otro inconveniente en el trabajo de CDS es que los archivos compartidos se proyectan en la memoria con direcciones fijas (generalmente a partir de 0x800000000
). Esto en sí mismo no es malo, pero dado que la Aleatorización del diseño del espacio de direcciones (ASLR) está habilitada de manera predeterminada en la mayoría de los sistemas operativos, el rango de memoria requerido puede estar parcialmente ocupado. Lo que hace el HotSpot JVM en este caso es la opción especial -Xshare
que admite tres valores:
-Xshare:on
- force CDS / AppCDS; Si el rango está ocupado, la JVM sale con un error. Este modo no se recomienda para su uso en producción , ya que puede provocar bloqueos esporádicos al iniciar aplicaciones.-Xshare:off
- (usted) cambia CDS / AppCDS; desactiva el uso de datos compartidos por completo (incluidos los archivos incrustados)-Xshare:auto
: el comportamiento predeterminado de la JVM cuando, en caso de imposibilidad de asignar el rango de memoria requerido, se entrega silenciosamente y carga las clases como de costumbre
Al momento de escribir este artículo, Oracle solo está trabajando para solucionar estos problemas, pero aún no se ha asignado un número de versión.
Estas opciones son parcialmente útiles para nosotros más adelante, pero por ahora veamos ...
Aplicaciones de AppCDS
Hay varias formas de hacerlo con AppCDS. arruinar tu vida optimizar el trabajo de microservicios. Varían mucho en complejidad y ganancias potenciales, por lo que es importante decidir de inmediato cuál se discutirá más adelante.
Lo más simple es usar ni siquiera AppCDS, sino solo CDS; esto es cuando solo las clases de plataforma entran en el archivo compartido (consulte "Una breve introducción a AppCDS"). Eliminaremos esta opción de inmediato, porque cuando se aplica a microservicios en Spring Boot, genera muy pocas ganancias. Esto se puede ver por la proporción del número de clases compartidas en su distribución general usando el ejemplo de un microservicio real (ver el segmento verde):

Más complejo, pero prometedor es el uso de AppCDS completo, es decir, la inclusión de las clases de biblioteca y aplicación en el mismo archivo. Esta es una familia completa de opciones que se deriva de combinaciones de la cantidad de aplicaciones participantes y la cantidad de instancias. Las siguientes son evaluaciones subjetivas de los autores sobre los beneficios y dificultades de varias aplicaciones de AppCDS.
Presta atención:
- En la aplicación para una aplicación en una instancia (No. 1), el beneficio de la memoria puede llegar a ser cero o incluso negativo (especialmente cuando se mide bajo Windows )
- La creación del archivo compartido correcto requiere acciones, cuya complejidad no depende de cuántas copias se lanzará la aplicación (compare los pares de opciones No. 1-2 y No. 3-4)
- Al mismo tiempo, la transición de una instancia a varias, obviamente, aumenta las ganancias para ambos indicadores, pero no afecta la complejidad de la preparación.
En este artículo, solo llegaremos a la opción n. ° 2 (a través del n. ° 1), ya que es lo suficientemente simple para un conocimiento cercano de AppCDS y solo sin trucos adicionales podemos usar los archivos JEP-350 Dynamic CDS lanzados recientemente, que quiero sentir en acción.
Archivos dinámicos de CDS
Los archivos JEP-350 Dynamic CDS, una de las principales innovaciones de Java 13, están diseñados para simplificar el uso de AppCDS. Para sentir la simplificación, primero debe comprender la complejidad. Permítame recordarle que el algoritmo clásico "limpio" para aplicar AppCDS consta de 3 pasos: (1) obtener una lista de clases compartidas, (2) crear un archivo a partir de ellas y (3) ejecutar la aplicación con el archivo conectado. De estos pasos, solo el tercero es realmente útil, el resto es solo preparación para ello. Y aunque obtener una lista de clases (paso 1) puede parecer muy simple (en algunos casos, incluso es opcional), de hecho, cuando se trabaja con aplicaciones no triviales, resulta ser la más difícil, especialmente con respecto a Spring Boot. Por lo tanto, se necesita JEP-350 solo para eliminar este paso, o mejor dicho, automatizarlo. La idea es que la propia JVM dibuje una lista de las clases que necesita la aplicación, y luego forme a sí misma el llamado archivo "dinámico". De acuerdo, suena bien. Pero el problema es que ahora no está claro en qué punto dejar de acumular clases y proceder a colocarlas en el archivo. Anteriormente, en el clásico AppCDS, elegíamos ese momento nosotros mismos e incluso podíamos meternos entre estas acciones para cambiar algo en la lista de clases antes de convertirlo en un archivo. Ahora esto está sucediendo automáticamente y solo en un momento, para el cual los ingenieros de JVM han elegido, quizás, la única opción de compromiso: el apagado regular de la JVM. Esto significa que el archivo no se creará hasta que la aplicación se detenga. Esta solución tiene un par de consecuencias importantes:
- En el caso de un bloqueo de JVM, el archivo no se creará, no importa cuán maravillosa sea la lista de clases acumuladas para entonces (no puede extraerla más tarde usando medios regulares).
- El archivo se creará solo a partir de aquellas clases que lograron cargarse durante la sesión de la aplicación. Para las aplicaciones web, esto significa que crear un archivo iniciando y deteniéndose allí mismo no es correcto, ya que muchas clases importantes no entrarán en el archivo. Es necesario ejecutar al menos una solicitud HTTP a la aplicación (y es mejor ejecutarla correctamente en todos los escenarios) para que se carguen todas las clases que realmente utiliza.
Una diferencia importante entre los archivos dinámicos y estáticos es que siempre representan un "complemento" sobre los archivos estáticos básicos, que pueden ser archivos integrados en el kit de distribución de Java o creados por separado en una forma clásica de 3 pasos.
Sintácticamente, el uso de Dynamic CDS Archives se reduce a dos lanzamientos de JVM con dos opciones:
-XX:ArchiveClassesAtExit=archive.jsa
prueba con la opción -XX:ArchiveClassesAtExit=archive.jsa
, al final del cual se creará un archivo dinámico (puede especificar cualquier ruta y nombre)- Lanzamiento útil con la opción
-XX:SharedArchiveFile=archive.jsa
, que utilizará el archivo creado anteriormente
La segunda opción no es diferente de conectar un archivo estático normal. Pero si de repente el archivo estático básico no está en la ubicación predeterminada (dentro del JDK), entonces esta opción también puede incluir una indicación de la ruta, por ejemplo:
-XX:SharedArchiveFile=base.jsa:dynamic.jsa
(en Windows, el separador de ruta debe ser el carácter ";")
Ahora ya sabe lo suficiente sobre AppCDS para poder verlo en acción.
Practica
Conejo experimental
Para que nuestra aplicación de AppCDS en la práctica no se limite a un típico HelloWorld, tomaremos como base la aplicación real en Spring Boot. Mis colegas y yo a menudo tenemos que ver registros de aplicaciones en servidores de prueba remotos y ver "en vivo", tal como están escritos. Para usar esto, un agregador de registro completo (como ELK) a menudo no es apropiado; descargar archivos de registro sin fin, durante mucho tiempo, y mirar la salida gris de la consola de la tail
es deprimente. Por lo tanto, creé una aplicación web que puede generar cualquier registro en tiempo real directamente en el navegador, colorear las líneas por nivel de importancia (al mismo tiempo formateando XML), agregar varios registros en uno, así como otros trucos. Se llama ANALÓGICO (como un "analizador de registros", aunque esto no es cierto) y se encuentra en GitHub . Haga clic en la captura de pantalla para ampliar:

Técnicamente, esta es una aplicación en Spring Boot + Spring Integration, bajo la cual kubectl
tail
, docker
y kubectl
(para admitir registros de archivos, contenedores Docker y recursos de Kubernetes, respectivamente). Viene en la forma del clásico archivo "grueso" Spring Boot JAR. En tiempo de ejecución, las clases K10K están colgadas en la memoria de la aplicación, de las cuales la gran mayoría son clases Spring y JDK. Obviamente, estas clases cambian muy raramente, lo que significa que pueden colocarse en un archivo compartido y reutilizarse en todas las instancias de la aplicación, ahorrando memoria y CPU.
Experimento individual
Ahora apliquemos el conocimiento existente de Dynamic AppCDS al conejo experimental. Como todo se sabe en comparación, necesitaremos algún punto de referencia: el estado del programa con el que compararemos los resultados obtenidos durante el experimento.
Observaciones introductorias
- Todos los comandos adicionales son para Linux. Las diferencias para Windows y macOS no son fundamentales.
- La compilación JIT puede afectar notablemente los resultados y, en teoría, por la pureza del experimento, podría desactivarse (con la opción
-Xint
, como se hizo en el artículo mencionado), pero en aras de la máxima credibilidad se decidió no hacerlo. - Los siguientes números sobre la hora de inicio se obtuvieron en un servidor de prueba rápida. En máquinas en funcionamiento, los números similares, por regla general, son más modestos, pero como no estamos interesados en valores absolutos, sino en incrementos porcentuales, consideramos que esta diferencia es insignificante.
- Para no entrar prematuramente en la complejidad de medir la memoria compartida, por ahora omitiremos obtener lecturas precisas en bytes. En su lugar, presentamos el concepto de " potencial de CDS " , expresado como un porcentaje del número de clases compartidas al número total de clases cargadas. Esto, por supuesto, es una cantidad abstracta, pero por otro lado, afecta directamente el consumo de memoria real; Además, su definición no depende en absoluto del sistema operativo, y para su cálculo, solo los registros son suficientes.
Punto de referencia
Deje que este punto sea el estado de una aplicación recién descargada, es decir sin el uso explícito de cualquier AppCDS'ov y otros. Para evaluarlo, necesitamos:
Instale OpenJDK 13 (por ejemplo, la distribución nacional de Liberica , pero no la versión lite).
También debe agregarse a la variable de entorno PATH o a JAVA_HOME
, por ejemplo, así:
export JAVA_HOME=~/tools/jdk-13
Descargue ANALOG (al momento de escribir este artículo, la última versión era v0.12.1).
Si es necesario, puede especificar en el archivo config/application.yaml
en el parámetro server.address
el nombre del host externo para acceder a la aplicación (de manera predeterminada, localhost
se especifica allí).
Habilite el registro de carga de clase JVM.
Para hacer esto, puede JAVA_OPTS
la variable de entorno JAVA_OPTS
con este valor:
export JAVA_OPTS=-Xlog:class+load=info:file=log/class-load.log
Esta opción se pasará a la JVM y le dice que se comprometa la fuente de cada clase.
Ejecute una prueba de funcionamiento:
- Ejecute la aplicación con el script
bin/analog
- Abra http: // localhost: 8083 en el navegador, presione botones y daws
- Detenga la aplicación presionando
Ctrl+C
en la consola de script bin/analog
Tome el resultado (de los archivos en el directorio log/
)
Número total de clases cargadas (por class-load.log
):
cat class-load.log | wc -l 10463
Cuántos de ellos se descargan de un archivo compartido (de acuerdo con él):
grep -o 'source: shared' - class-load.log 1146
Tiempo de inicio promedio (después de una serie de inicios; por analog.log
):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.5225
Entonces, en este paso, el potencial de CDS era 1146/10463=0,1095
± 11% . Si le sorprende de dónde provienen las clases compartidas (después de todo, todavía no hemos incluido ningún AppCDS), entonces le recuerdo que a partir de la versión 12, el JDK incluye el archivo CDS terminado $JAVA_HOME/lib/server/classes.jsa
, construido por nada menos que lista lista de clases:
cat $JAVA_HOME/lib/classlist | wc -l 1170
Ahora, habiendo evaluado el estado inicial de la aplicación, podemos aplicarle AppCDS y, en comparación, entender lo que esto proporciona.
Experiencia central
Como nos dejó la documentación , para crear un archivo dinámico AppCDS, debe realizar solo una ejecución de prueba de la aplicación con la opción -XX:ArchiveClassesAtExit
. Desde el próximo lanzamiento, el archivo se puede usar y recibir ganancias. Para verificar esto en el mismo conejo experimental (AnaLog), necesita:
Agregue la opción especificada al comando de ejecución:
export JAVA_OPTS="$JAVA_OPTS -XX:ArchiveClassesAtExit=work/classes.jsa"
Extender el registro:
export JAVA_OPTS="$JAVA_OPTS -Xlog:cds=debug:file=log/cds.log"
Esta opción forzará el proceso de creación de un archivo CDS para que se registre cuando se detenga la aplicación.
Realice la misma prueba que con el punto de referencia:
- Ejecute la aplicación con el script
bin/analog
- Abra http: // localhost: 8083 en el navegador, presione botones y daws
- Detenga la aplicación presionando
Ctrl+C
en la consola de script bin/analog
Después de eso, una tremenda plantilla con todo tipo de advertencia debería caer en la consola, y el log/cds.log
debería llenarse de detalles; Todavía no nos interesan.
Cambie el modo de inicio de prueba a útil:
export JAVA_OPTS="-XX:SharedArchiveFile=work/classes.jsa -Xlog:class+load=info:file=log/class-load.log -Xlog:class+path=debug:file=log/class-path.log"
Aquí no complementamos la variable JAVA_OPTS
, sino que la JAVA_OPTS
a borrar con nuevos valores que incluyen (1) usar un archivo compartido, (2) registrar fuentes de clase y (3) registrar comprobaciones de ruta de clase.
Realice un lanzamiento útil de la aplicación de acuerdo con el esquema del párrafo 3.
Tome el resultado (de los archivos en el directorio log/
)
Verificando que AppCDS realmente se aplica (por el class-path.log
):
[0.011s][info][class,path] type=BOOT [0.011s][info][class,path] Expecting BOOT path=/home/upc/tools/jdk-13/lib/modules [0.011s][info][class,path] ok [0.011s][info][class,path] type=APP [0.011s][info][class,path] Expecting -Djava.class.path=/home/upc/tmp/analog/lib/analog.jar [0.011s][info][class,path] ok
Las marcas ok
después de las líneas type=BOOT
y type=APP
indican la apertura, verificación y carga exitosas de los archivos CDS incorporados y aplicados, respectivamente.
Número total de clases cargadas (por class-load.log
):
cat class-load.log | wc -l 10403
Cuántos de ellos se descargan de un archivo compartido (de acuerdo con él):
grep -o 'source: shared' -c class-load.log 6910
Tiempo promedio de inicio (después de una serie de inicios; por archivo analog.log
):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.04167
Pero en este paso, el potencial de CDS ya era 6910/10403≈0,66
= 66% , es decir, aumentó en un 55% en comparación con el punto de referencia. Al mismo tiempo, el tiempo de lanzamiento promedio se redujo en (4,5225-4,04167)=0,48
segundos, es decir El inicio es más rápido en un ± 10,6% del valor inicial.
Análisis de resultados
El título de trabajo del artículo es: "¿Por qué tan poco?"
Nosotros, como, hicimos todo de acuerdo con las instrucciones, pero no todas las clases estaban en el archivo. Su número afecta el tiempo de lanzamiento no menos que la potencia de cálculo de la máquina del experimentador, por lo que nos concentraremos en este número.
Si recuerdas, ignoramos el archivo log/cds.log
creado durante la detención de la aplicación experimental después de la ejecución de prueba. En este archivo HotSpot, la JVM señaló amablemente las clases de advertencia para cada clase que no aparecían en el archivo CDS. Aquí está el número total de tales marcas:
grep -o '[warning]' cds.log -c 3591
Teniendo en cuenta que solo se mencionan 10K + clases en el registro class-load.log
y el 66% de ellas se cargan desde el archivo comprimido, no es difícil entender que las 3600 clases enumeradas en cds.log
son el 44% "perdido" del potencial CDS. Ahora debe averiguar por qué se omitieron.
Si observa el registro de cds.log, resulta que solo hay 4 razones únicas para omitir clases. Aquí hay ejemplos de cada uno de ellos:
Skipping org/springframework/web/client/HttpClientErrorException: Not linked Pre JDK 6 class not supported by CDS: 49.0 org/jrobin/core/RrdUpdater Skipping java/util/stream/Collectors$$Lambda$554: Unsafe anonymous class Skipping ch/qos/logback/classic/LoggerContext: interface org/slf4j/ILoggerFactory is excluded
Entre las 3591 clases perdidas, estas razones se encuentran aquí con tanta frecuencia:

Míralos más de cerca:
Unsafe anonymous class
JVM “” , -, .
Not linked
, “” , , . , StackOverflow . , , “” () JAR- , AppCDS. , ( ).
Pre JDK 6 class
, CDS Java 5. class- , CDS . , , 6, Java, . - , runtime- (, slf4j).
Skipping ... : super class/interface ... is excluded
, “” . CDS', . Por ejemplo:
[warning][cds] Pre JDK 6 class not supported by CDS: 49.0 org/slf4j/spi/MDCAdapter [warning][cds] Skipping ch/qos/logback/classic/util/LogbackMDCAdapter: interface org/slf4j/spi/MDCAdapter is excluded
Conclusión
CDS 100%.
, , , , , . .
JEP-310 , AppCDS JDK. . , . CDS (, , ) .
Para clonar el conejo experimental (ejecutar AnaLog en varios casos), necesitamos cambiar algo en la configuración; esto permitirá que los procesos levantados no se "acoplen". Gracias a Spring Boot, puede hacer esto sin editar o copiar ningún archivo; cualquier configuración puede ser anulada por las opciones de JVM. El reenvío de estas opciones desde la variable de entorno ANALOG_OPTS
proporciona un script de inicio, generosamente generado por Gradle.
export ANALOG_OPTS="-Djavamelody.enabled=false -Dlogging.config=classpath:logging/logback-console.xml" export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
JavaMelody, , , . TCP- ; .
, , JVM AppCDS . JAVA_OPTS
JVM Unified Logging Framework :
export JAVA_OPTS="-Xlog:class+load=info:file=log/class-load-%p.log -Xlog:class+path=debug:file=log/class-path-%p.log" export JAVA_OPTS="$JAVA_OPTS -XX:SharedArchiveFile=work/classes.jsa"
%p
, JVM (PID). AppCDS , ( ).
, . . :
server.port
nodes.this.agentPort
, :
export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
, ( ).
bin/analog
() http://localhost:8091 ,
PID ( ), :
pgrep -f analog 13792
pmap
( ):
pmap -XX 13792 | sed -n -e '2p;$p' Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked ProtectionKey VmFlagsMapping 3186952 1548 1548 328132 325183 3256 0 10848 314028 212620 314024 0 0 0 0 0 0 0 325183 0 KB
; .
1-4 (, ).
pmap
. CDS' . , , PSS:
The "proportional set size" (PSS) of a process is the count of pages it has in memory, where each page is divided by the number of processes sharing it. So if a process has 1000 pages all to itself, and 1000 shared with one other process, its PSS will be 1500.
, , “ ” . , .
PSS , :
, - :
, . AppCDS. , -XX:SharedArchiveFile=work/classes.jsa
-Xshare:off
, CDS . , .

:
PSS AppCDS CDS.
. , , HelloWorld- JVM CDS 2 , CDS. PSS CDS, . :
PSS AppCDS 2- ; 3- .
, , , . , AppCDS, , , 3- .
: , CDS? :
CDS/AppCDS JVM , PSS . , , pmap
, “” sed
'. :
pmap -X `pgrep -f analog` 14981:
( Mapping
) , “” . JVM ( libjvm.so
), ( libc-2.27.so
). :
For the Java VM, the read-only parts of the loaded shared libraries (ie libjvm.so
) can be shared between all the VM instances running at the same time. This explains why, taking together, the two VM's consume less memory (ie have a smaller memory footprint) than the simple sum of their single resident set sizes when running alone.
. , , . , , JVM , Java- . GeekOut:

, , , AppCDS , .. Java-. , JVM, , - .
VisualVM Metaspace AppCDS , :
AppCDS

AppCDS

, 128 Metaspace AppCDS 64.2 MiB / 8.96 MiB
≈7,2 , CDS . (. ) 66.4 MiB / 13.9 MiB
≈4,8 . , AppCDS , Metaspace. Metaspace, , CDS .
En lugar de una conclusión
Spring Boot AppCDS – JVM, .
- JEP-350 Dynamic CDS Archives – JDK 13.
- Spring Boot ó CDS ( ). , 100% - 66% . , ≈11% ( 15%, ).
- , 5- PSS ( ). , AppCDS , , 8% (PSS). , CDS, , . AppCDS .
- Metaspace, , AppCDS 5 , CDS.
, , AppCDS, , “killer feature”. Spring Boot. , , AppCDS . , , AppCDS Spring Boot. , …
by Nick Fewings on Unsplash