Profiler des applications Java

Cette page explique comment modifier votre application Java pour capturer les données de profilage et les envoyer à votre projet Google Cloud. Pour obtenir des informations générales sur le profilage, consultez la page Concepts du profilage.

Types de profil pour Java :

  • Temps CPU
  • Tas de mémoire (nécessite Java 11 ou l'environnement standard App Engine, désactivé par défaut)
  • Durée d'exécution (non disponible pour l'environnement standard App Engine Java 8)

Versions de langages compatibles avec Java :

  • JVM basées sur HotSpot (y compris JDK Oracle et certains builds OpenJDK) pour Java 8, 11 ou version ultérieure

Versions d'agent de profilage compatibles :

  • La version la plus récente de l'agent est compatible. En général, les versions datant de plus d'un an ne sont pas compatibles. Nous vous recommandons d'utiliser la version la plus récente de l'agent.

Systèmes d'exploitation compatibles :

  • Linux. Le profilage d'applications Java est compatible avec les noyaux Linux dont la bibliothèque C standard est mise en œuvre avec glibc ou musl. Pour obtenir des informations de configuration spécifiques aux noyaux Linux Alpine, reportez-vous à la page Exécution sous Linux Alpine.

Environnements compatibles :

Activer l'API Profiler

Avant d'utiliser l'agent de profilage, assurez-vous que l'API Profiler sous-jacente est activée. Vous pouvez vérifier l'état de l'API et l'activer, si nécessaire, à l'aide de Google Cloud CLI ou de Google Cloud Console :

CLI gcloud

  1. Si vous n'avez pas encore installé Google Cloud CLI sur votre poste de travail, consultez la documentation de Cloud CLI.

  2. Exécutez la commande suivante :

    gcloud services enable cloudprofiler.googleapis.com
    

Pour en savoir plus, consultez les sections sur gcloud services

console Google Cloud

  1. Enable the required API.

    Enable the API

  2. Si API activée s'affiche, cela signifie que l'API est déjà activée. Sinon, cliquez sur le bouton Activer.

Installer l'agent Profiler

Compute Engine

  1. Créez un répertoire d'installation, par exemple /opt/cprof, pour l'agent Profiler :

     sudo mkdir -p /opt/cprof

  2. Téléchargez l'archive de l'agent à partir du dépôt storage.googleapis.com et extrayez-la dans le répertoire d'installation :

    wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
    | sudo tar xzv -C /opt/cprof

GKE

Modifiez le fichier Dockerfile afin de créer un répertoire d'installation pour l'agent Profiler. Ensuite, téléchargez l'archive de l'agent, puis extrayez-la dans le répertoire d'installation.

Linux (bibliothèque C basée sur glibc) :

Utilisez la commande d'installation suivante :

RUN mkdir -p /opt/cprof && \
  wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
  | tar xzv -C /opt/cprof

Linux Alpine (bibliothèque C basée sur musl) :

Utilisez la commande d'installation suivante :

wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent_alpine.tar.gz \
| tar xzv -C /opt/cprof

Environnement flexible

Lorsque vous utilisez l'image de base de l'environnement d'exécution Java 8 de Google ou l'image de base de l'environnement d'exécution Java 9/Jetty 9, l'agent Profiler est préinstallé. Par conséquent, aucune étape supplémentaire n'est requise pour installer l'agent.

Toutes les autres images de base nécessitent l'installation de l'agent. Par exemple, le fichier Dockerfile suivant contient les instructions permettant d'utiliser l'image openjdk:11-slim et d'installer l'agent Profiler. Ce fichier définit également les paramètres par défaut à utiliser lors du démarrage de l'application :

FROM openjdk:11-slim

COPY . .
RUN  apt-get update \
     && apt-get install wget \
     && rm -rf /var/lib/apt/lists/*

RUN mkdir -p /opt/cprof && \
    wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
    | tar xzv -C /opt/cprof

CMD ["java", "-agentpath:/opt/cprof/profiler_java_agent.so=OPTION1,OPTION2", "-jar", "PATH_TO_YOUR_JAR_FILE"]

Pour utiliser ce fichier Dockerfile avec l'environnement flexible App Engine, procédez comme suit :

  • Remplacez OPTION1 et OPTION2 par les valeurs de configuration de l'agent requises pour votre application, puis remplacez PATH_TO_YOUR_JAR_FILE par le chemin d'accès à votre fichier JAR.
  • Placez le fichier Dockerfile dans le même répertoire que votre fichier app.yaml.
  • Modifiez le fichier app.yaml pour spécifier un environnement d'exécution personnalisé. Pour en savoir plus, consultez la page Créer des environnements d'exécution personnalisés.

Environnement standard

Lorsque vous utilisez l'environnement d'exécution Java, l'agent Profiler est préinstallé. Par conséquent, aucune étape supplémentaire n'est requise pour installer l'agent. Pour les versions Java 11 et ultérieures, il est préinstallé dans /opt/cprof.

En dehors de Google Cloud

  1. Créez un répertoire d'installation, par exemple /opt/cprof, pour l'agent Profiler :

     sudo mkdir -p /opt/cprof

  2. Téléchargez l'archive de l'agent à partir du dépôt storage.googleapis.com et extrayez-la dans le répertoire d'installation :

    wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
    | sudo tar xzv -C /opt/cprof

Pour répertorier toutes les versions de l'agent disponibles au téléchargement, exécutez la commande suivante :

gcloud storage ls gs://cloud-profiler/java/cloud-profiler-*

Le résultat de la commande ressemble à ce qui suit :

gs://cloud-profiler/java/cloud-profiler-java-agent_20191014_RC00.tar.gz
gs://cloud-profiler/java/cloud-profiler-java-agent_20191021_RC00.tar.gz
gs://cloud-profiler/java/cloud-profiler-java-agent_20191028_RC00.tar.gz

Pour télécharger une version spécifique de l'agent, transmettez son URL à la commande de téléchargement. Par exemple, pour télécharger l'agent créé le 28 octobre 2019, utilisez l'instruction suivante :

wget -q -O- https://storage.googleapis.com/cloud-profiler/java/cloud-profiler-java-agent_20191028_RC00.tar.gz \
  | sudo tar xzv -C /opt/cprof

La version de l'agent est enregistrée lors de son initialisation.

Charger l'agent Profiler

Pour profiler votre application, démarrez Java comme vous le feriez normalement pour exécuter votre programme, mais spécifiez les options de configuration de l'agent. Lorsque vous indiquez le chemin d'accès à la bibliothèque de l'agent, vous pouvez transmettre des options à cette dernière.

Pour l'environnement standard App Engine, l'agent est chargé et configuré automatiquement. Pour en savoir plus sur la configuration et le démarrage de votre programme, consultez la section Démarrer votre programme.

Configurer l'agent

Pour configurer l'agent de profilage, incluez l'option -agentpath lors du démarrage de votre application :

 -agentpath:INSTALL_DIR/profiler_java_agent.so=OPTION1,OPTION2,OPTION3

Dans cette expression, INSTALL_DIR est le chemin d'accès de l'agent de profilage, et OPTION1, OPTION2 et OPTION3 sont des options de configuration de l'agent. Par exemple, si vous remplacez l'option OPTION1 par -cprof_service=myapp dans l'expression précédente, vous définissez le nom du service sur myapp. L'ordre et le nombre des options ne sont soumis à aucune limite. Les options de configuration compatibles sont répertoriées dans le tableau suivant :

Option de l'agent Description
-cprof_service Si votre application n'est pas exécutée sur App Engine, cette option de configuration vous permet de définir le nom du service. Pour connaître les restrictions relatives au nom de service, reportez-vous à la section Arguments de nom et de version de service.
-cprof_service_version Lorsque vous souhaitez pouvoir analyser des données de profilage à l'aide de l'interface utilisateur de Profiler avec la version du service, spécifiez la version à l'aide de cette option Pour connaître les restrictions relatives à la version, reportez-vous à la section Arguments de nom et de version de service.
-cprof_project_id Pour les exécutions en dehors de Google Cloud, spécifiez l'ID de votre projet Google Cloud à l'aide de cette option. Pour en savoir plus, consultez la page Profiler des applications s'exécutant en dehors de Google Cloud.
-cprof_zone_name Lorsque votre application s'exécute sur Google Cloud, l'agent de profilage détermine la zone en communiquant avec le service de métadonnées Compute Engine. Si l'agent de profilage ne peut pas communiquer avec le service de métadonnées, vous devez utiliser cette option.
-cprof_gce_metadata_server_retry_count
-cprof_gce_metadata_server_retry_sleep_sec
Utilisées conjointement, ces deux options définissent le règlement relatif aux tentatives d'exécution suivi par l'agent Profiler lorsqu'il communique avec le service de métadonnées de Compute Engine pour recueillir l'ID de votre projet Google Cloud et les informations concernant la zone.

Le règlement par défaut consiste en trois tentatives à une seconde d'intervalle. Ce règlement suffit pour la plupart des configurations.
-cprof_cpu_use_per_thread_timers Pour les profils de temps CPU les plus précis, définissez cette option sur "true" (vrai). L'utilisation de cette option entraîne une augmentation des coûts par thread.

La valeur par défaut est "false" (faux).
-cprof_force_debug_non_safepoints Par défaut, l'agent de profilage oblige la JVM à générer des informations de débogage pour l'intégralité du code généré juste-à-temps (JIT, Just-In-Time), en plus de générer des informations de débogage pour tous les points de restauration. En conséquence, vous obtenez des informations extrêmement précises sur la fonction et sur l'emplacement au niveau de la ligne pour les profils de tas de mémoire et de Temps CPU, en échange de coûts supplémentaires. Pour désactiver les informations de débogage relatives au code juste-à-temps (JIT, Just-In-Time), définissez cette option sur "false" (faux).

La valeur par défaut est "true" (vrai).
-cprof_wall_num_threads_cutoff Par défaut, les profils de durée d'exécution ne sont pas récupérés si le nombre total de threads dans l'application est supérieur à 4 096. Cette limite garantit un coût minime spécifique à l'acheminement de la pile de threads lors de la collecte de profils Si votre service comporte normalement plus de 4 096 threads et si vous acceptez de payer des coûts supplémentaires pour collecter des données de profilage, vous pouvez augmenter cette limite à l'aide de cette option.

La limite par défaut est de 4 096 threads.
-cprof_enable_heap_sampling Pour activer le profilage de tas de mémoire pour Java 11 ou une version supérieure, définissez
-cprof_enable_heap_sampling=true. Le profilage de tas de mémoire n'est pas compatible avec Java 10 ou une version antérieure.

Le profilage de tas de mémoire est désactivé par défaut.

Lorsque vous activez le profilage de tas de mémoire, l'intervalle d'échantillonnage est défini sur 512 Kio par défaut. Cet intervalle suffit pour la plupart des applications et implique un coût inférieur à 0,5 % pour l'application. Les intervalles d'échantillonnage compris entre 256 Kio (262144) et 1024 Kio (1048576) sont compatibles. Par exemple, pour définir l'intervalle d'échantillonnage sur 256 Kio, et ainsi doubler le taux d'échantillonnage, ajoutez l'option de l'agent suivante :
-cprof_heap_sampling_interval=262144
De même, pour définir l'intervalle d'échantillonnage sur 1 024 Kio, et ainsi réduire de moitié le taux d'échantillonnage, ajoutez l'option de l'agent suivante :
-cprof_heap_sampling_interval=1048576
Si vous activez ce type de profil, spécifiez une nouvelle version de service lorsque vous déployez votre application. Pour en savoir plus, consultez l'article Pourquoi est-ce que je ne n'obtiens pas de données pour un type de profil spécifique ?.

Arguments de nom et de version de service

Lorsque vous chargez l'agent Profiler, vous devez spécifier un argument de nom de service et un argument facultatif de version de service pour le configurer.

Le nom de service permet à Profiler de collecter des données de profilage pour toutes les instances dupliquées de ce service. Le service du profileur assure un taux de collecte de l'ordre d'un profil par minute, en moyenne, et ce pour chaque nom de service dans chaque combinaison de zones et de versions de service.

Par exemple, si deux versions d'un service s'exécutent sur des instances dupliquées dans trois zones, le profileur créera en moyenne six profils par minute pour ce service.

Si vous utilisez différents noms de service pour vos instances dupliquées, votre service sera profilé plus souvent que nécessaire, entraînant une surcharge proportionnelle.

Lorsque vous sélectionnez un nom de service, respectez ces instructions :

  • Choisissez un nom qui représente clairement le service dans votre architecture d'application. Le choix du nom de service est moins important si vous n'exécutez qu'un seul service ou qu'une seule application. En revanche, il est plus important lorsque votre application s'exécute sous la forme d'un ensemble de microservices, par exemple.

  • Assurez-vous de ne pas utiliser de valeurs spécifiques au processus, telles qu'un ID de processus, dans la chaîne du nom de service.

  • La chaîne du nom de service doit correspondre à l'expression régulière ci-dessous :

    ^[a-z0-9]([-a-z0-9_.]{0,253}[a-z0-9])?$

Nous vous recommandons d'utiliser une chaîne statique, telle que imageproc-service, comme nom de service.

La version de service est facultative. Si vous la spécifiez, Profiler peut regrouper les informations de profilage de plusieurs instances et les afficher correctement. Vous pouvez spécifier ce paramètre pour marquer différentes versions de vos services au fur et à mesure de leur déploiement. L'interface utilisateur de Profiler vous permet de filtrer les données par version de service. De cette façon, vous pouvez comparer les performances des versions de code les plus anciennes et les plus récentes.

La valeur de l'argument service-version est une chaîne de forme libre, mais les valeurs de cet argument ressemblent généralement à des numéros de version, par exemple 1.0.0 ou 2.1.2.

Démarrer votre programme

Compute Engine

Démarrez Java comme vous le feriez normalement pour exécuter votre programme, mais spécifiez les options de configuration de l'agent comme suit :

java \
    -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=myapp,-cprof_service_version=1.0.0 \
    JAVA_OPTIONS -jar PATH_TO_YOUR_JAR_FILE PROGRAM_OPTIONS

GKE

Modifiez le Dockerfile du conteneur de service pour qu'il démarre Java comme vous le feriez normalement pour exécuter votre programme, puis ajoutez les options de configuration de l'agent indiquées ci-dessous :

CMD ["java", \
    "-agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=myapp,-cprof_service_version=1.0.0", \
     "-jar", "PATH_TO_YOUR_JAR_FILE" ]
    

Environnement flexible

Modifiez le fichier de configuration app.yaml en définissant la variable d'environnement PROFILER_ENABLE. Démarrez ensuite votre programme comme d'habitude :

env_variables:
   PROFILER_ENABLE: true

Pour en savoir plus, consultez la section Définir des variables d'environnement.

Environnement standard

Environnement d'exécution Java 21

Si vous n'utilisez pas d'anciens services groupés, activez la collecte du profileur en modifiant le fichier app.yaml pour spécifier l'option agentpath à l'aide de l'une des méthodes suivantes :

  • Définissez le paramètre JAVA_TOOL_OPTIONS:

    runtime: java21
    env_variables:
      JAVA_TOOL_OPTIONS: "-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true"
    
  • Spécifiez agentpath à l'aide de l'élément entrypoint:

    runtime: java21
    entrypoint: java \
      -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true \
      Main.java
    

Si vous utilisez d'anciens services groupés, activez la collection du profileur en modifiant le Fichier appengine-web.xml pour spécifier l'option agentpath à l'aide de l'une des méthodes suivantes:

  • Définissez la variable d'environnement JAVA_USER_OPTS :

    <?xml version="1.0" encoding="utf-8"?>
    <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <env-variables>
    <env-var name="JAVA_USER_OPTS" value="-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true" />
    </env-variables>
    </appengine-web-app>
  • Définissez le paramètre CPROF_ENABLE:

    <?xml version="1.0" encoding="utf-8"?>
    <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <env-variables>
    <env-var name="CPROF_ENABLE" value="-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true" />
    </env-variables>
    </appengine-web-app>
  • Spécifiez le agentpath à l'aide de l'élément entrypoint :

    <?xml version="1.0" encoding="utf-8"?>
    <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
      <entrypoint>
       java
       -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true
      </entrypoint>
    </appengine-web-app>

Si vous configurez un nouveau type de profil pour la collecte, assurez-vous de spécifier une nouvelle version du service lorsque vous déployez votre application. Pour plus d'informations, consultez la section Pourquoi est-ce que je ne dispose d'aucune donnée pour un type de profil spécifique ?

Journaliser avec l'agent

L'agent de profilage peut collecter des informations de journalisation pour l'environnement flexible App Engine, Compute Engine et GKE. L'agent de profilage est compatible avec les niveaux de journalisation suivants :

  • 0 : journalise tous les messages. Niveau de journalisation par défaut.
  • 1 : journalise les messages d'avertissement, les messages d'erreur et les messages d'erreur fatale.
  • 2 : journalise les messages d'erreur et les messages d'erreur fatale.
  • 3 : ne journalise que les messages d'erreur fatale et arrête l'application.

Pour activer l'écriture de journaux sur une erreur standard avec le niveau de journalisation par défaut, ajoutez l'option -logtostderr à la configuration -agentpath.

Pour que le niveau de journalisation n'enregistre que les messages d'erreur et les messages d'erreur fatale, ajoutez l'option -minloglevel=2 à la configuration -agentpath.

Par exemple, pour activer la journalisation des messages d'erreur et les messages d'erreur fatale sur l'erreur standard, ajoutez les options -logtostderr et ‑minloglevel=2 à la configuration -agentpath :

 java -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=myapp,-logtostderr,-minloglevel=2 \
   -jar myApp.jar

Dépannage

Dans cette section, vous trouverez la liste des problèmes spécifiques au profilage des applications Java. Consultez la page Dépannage pour obtenir de l'aide concernant les problèmes courants.

Comportement Cause Solution
Vous avez activé plusieurs profileurs de segments de mémoire et vous n'obtenez aucune donnée de profil. L'utilisation simultanée de plusieurs profileurs de segments de mémoire désactive toute compatibilité de profilage de segments de mémoire pour Java. Il s'agit là d'une limitation de la machine virtuelle Java. N'activez qu'un seul profileur.

Exécuter avec Linux Alpine

L'agent de profilage Java pour Linux Alpine n'est compatible qu'avec les configurations Google Kubernetes Engine.

Pour installer la dernière version de l'agent de profilage Java pour Linux Alpine, consultez Installer l'agent Profiler

Erreur d'authentification

Si vous utilisez des images Docker exécutées avec Linux Alpine (par exemple, golang:alpine ou simplement alpine), l'erreur d'authentification suivante peut s'afficher :

connection error: desc = "transport: authentication handshake failed: x509: failed to load system roots and no roots provided"

Sachez que pour que cette erreur apparaisse, vous devez avoir activé la journalisation avec l'agent.

L'erreur indique que lorsque les images Docker sont exécutées avec Linux Alpine, les certificats SSL racine ne sont pas installés par défaut. Ces certificats sont nécessaires pour que l'agent de profilage puisse communiquer avec l'API du profileur. Pour résoudre cette erreur, ajoutez la commande apk suivante à votre fichier Dockerfile :

FROM alpine
...
RUN apk add --no-cache ca-certificates

Vous devez ensuite recompiler et redéployer votre application.

Étape suivante