Partager via


Gestion de la mémoire Java

Remarque

Les plans Essentiel, Standard et Entreprise seront déconseillés à compter de la mi-mars 2025, avec une période de mise hors service de 3 ans. Nous vous recommandons de passer à Azure Container Apps. Pour plus d’informations, consultez l’annonce de mise hors service d’Azure Spring Apps.

Le plan de consommation standard et dédiée sera déconseillé à compter du 30 septembre 2024, avec un arrêt complet après six mois. Nous vous recommandons de passer à Azure Container Apps. Pour plus d’informations, consultez Migrer le plan de consommation standard et dédiée Azure Spring Apps vers Azure Container Apps.

Cet article s’applique à : ✅ Essentiel/Standard ❎ Entreprise

Cet article décrit différents concepts liés à la gestion de la mémoire Java pour vous aider à comprendre le comportement des applications Java hébergées dans Azure Spring Apps.

Modèle de mémoire Java

La mémoire d’une application Java comporte plusieurs parties et il existe différentes façons de diviser les parties. Cet article traite de la mémoire Java divisée en mémoire segmentée, en mémoire non-segmentée et en mémoire directe.

Mémoire segmentée

La mémoire segmentée stocke toutes les instances de classe et les tableaux. Chaque machine virtuelle Java (JVM) n’a qu’une seule zone segmentée, qui est partagée entre les threads.

Spring Boot Actuator peut observer la valeur de la mémoire segmentée. Spring Boot Actuator prend la valeur segmentée dans le cadre de jvm.memory.used/committed/max. Pour plus d’informations, consultez la section jvm.memory.used/commit/max dans Outils de résolution des problèmes de mémoire.

La mémoire segmentée est divisée en jeune génération et en ancienne génération. Ces termes sont décrits dans la liste suivante, ainsi que les termes connexes.

  • Jeune génération: tous les nouveaux objets sont alloués et âgés en jeune génération.

    • Espace Eden : de nouveaux objets sont alloués dans l’espace Eden.
    • Espace survivant : les objets seront déplacés d’Eden à l’espace survivant après avoir survécu à un cycle de nettoyage de la mémoire. L’espace survivant peut être divisé en deux parties : s1 et s2.
  • Ancienne génération: également appelé espace foncier. Les objets qui sont restés dans les espaces survivants pendant longtemps seront déplacés vers l’ancienne génération.

Avant Java 8, une autre section appelée génération permanente faisait également partie du segment. À compter de Java 8, la génération permanente a été remplacée par le métaspace dans la mémoire non segmentée.

Mémoire non segmentée

La mémoire non segmentée se divise selon les parties suivantes :

  • Partie de la mémoire non segmentée qui a remplacé la génération permanente (ou permGen) à partir de Java 8. Spring Boot Actuator observe cette section et la prend dans le cadre de jvm.memory.used/committed/max. En d’autres termes, jvm.memory.used/committed/max est la somme de la mémoire segmentée et de l’ancien permGen partie de la mémoire non segmentée. L’ancienne génération permanente est composée des parties suivantes :

    • Metaspace, qui stocke les définitions de classe chargées par les chargeurs de classes.
    • Espace de classe compressé, qui est destiné aux pointeurs de classe compressés.
    • Cache de code, qui stocke le code natif compilé par JIT.
  • D’autres mémoires telles que la pile de threads, qui n’est pas observée par Spring Boot Actuator.

Mémoire directe

La mémoire directe est allouée en mémoire native par java.nio.DirectByteBuffer, qui est utilisée dans les bibliothèques tierces telles que nio et gzip.

Spring Boot Actuator n’observe pas la valeur de la mémoire directe.

Le diagramme suivant récapitule le modèle de mémoire Java décrit dans la section précédente.

Diagramme montrant le modèle de mémoire Java.

Nettoyage de la mémoire Java

Il existe trois termes concernant le nettoyage de la mémoire Java (GC) : « GC mineur », « Major GC » et « Full GC ». Ces termes ne sont pas clairement définis dans la spécification JVM. Ici, nous considérons que « Major GC » et « Full GC » sont équivalents.

Le GC mineur s’exécute lorsque l’espace Eden est plein. Il supprime tous les objets morts de jeune génération et déplace les objets vivants vers l’espace Eden vers s1 de l’espace survivant, ou de s1 à s2.

GC complet ou GC majeur effectue le nettoyage de la mémoire dans l’ensemble du segment. Le GC complet peut également collecter des parties telles que le métaspace et la mémoire directe, qui peuvent être nettoyées uniquement par GC complet.

La taille maximale du segment influence la fréquence du GC mineur et du GC complet. Le métaspace maximal et la taille maximale de mémoire directe influencent le GC complet.

Lorsque vous définissez la taille maximale du segment sur une valeur inférieure, les collections de mémoires se produisent plus fréquemment, ce qui ralentit un peu l’application, mais limite mieux l’utilisation de la mémoire. Lorsque vous définissez la taille maximale du segment sur une valeur supérieure, les regroupements de mémoires se produisent moins fréquemment, ce qui peut créer un risque plus élevé en mémoire (OOM). Pour plus d’informations, consultez la section Types de problèmes hors mémoire des Problèmes de redémarrage d’application causés par des problèmes de mémoire insuffisante.

L’espace métaspace et la mémoire directe peuvent être collectés uniquement par GC complet. Lorsque la métaspace ou la mémoire directe est pleine, le GC complet se produit.

Configurations de mémoire Java

Les sections suivantes décrivent les aspects importants de la configuration de la mémoire Java.

Conteneurisation Java

Les applications dans Azure Spring Apps s’exécutent dans des environnements de conteneur. Pour plus d’informations, consultez Conteneurisez vos applications Java.

Options JVM importantes

Vous pouvez configurer la taille maximale de chaque partie de la mémoire à l’aide des options JVM. Vous pouvez définir des options JVM à l’aide de commandes Azure CLI ou via le Portail Azure. Pour plus d’informations, consultez la section Modifier les configurations pour résoudre les problèmes des Outils pour résoudre les problèmes de mémoire.

La liste suivante décrit les options JVM :

  • Configuration de la taille du segment

    • -Xms définit la taille initiale du segment par valeur absolue.
    • -Xmx définit la taille maximale du segment par valeur absolue.
    • -XX:InitialRAMPercentage définit la taille initiale du segment par le pourcentage de taille du segment/taille de mémoire de l’application.
    • -XX:MaxRAMPercentage définit la taille maximale du segment par le pourcentage de taille du segment/taille de mémoire de l’application.
  • Configuration de la taille de la mémoire directe

    • -XX:MaxDirectMemorySize définit la taille maximale de mémoire directe par valeur absolue. Pour plus d’informations, consultez MaxDirectMemorySize dans la documentation Oracle.
  • Configuration de la taille du métaspace

    • -XX:MaxMetaspaceSize définit la taille maximale du métaspace par valeur absolue.

Taille maximale de mémoire par défaut

Les sections suivantes décrivent comment les tailles de mémoire maximales par défaut sont définies.

Taille de segment maximale par défaut

Azure Spring Apps définit la taille maximale de mémoire de segment par défaut sur environ 50 %-80 % de la mémoire d’application pour les applications Java. Plus précisément, Azure Spring Apps utilise les paramètres suivants :

  • Si la mémoire < de l’application est de 1 Go, la taille maximale de segment par défaut est de 50 % de la mémoire de l’application.
  • Si 1 Go <= la mémoire de l’application < 2 Go, la taille maximale de segment par défaut est de 60 % de la mémoire de l’application.
  • Si 2 Go <= la mémoire de l’application < 3 Go, la taille maximale de segment par défaut est de 70 % de la mémoire de l’application.
  • Si 3 Go <= la mémoire de l’application, la taille maximale de segment par défaut est de 80 % de la mémoire de l’application.

Taille maximale de mémoire directe par défaut

Lorsque la taille maximale de mémoire directe n’est pas définie à l’aide des options JVM, la machine virtuelle JVM définit automatiquement la taille maximale de mémoire directe sur la valeur retournée par Runtime.getRuntime.maxMemory(). Cette valeur est approximativement égale à la taille maximale de mémoire du segment. Pour plus d’informations, consultez le fichier JDK 8 VM.java.

Disposition de l’utilisation de la mémoire

La taille du segment est influencée par votre débit. En fait, lors de la configuration, vous pouvez conserver la taille maximale de segment par défaut, ce qui laisse une mémoire raisonnable pour d’autres parties.

La taille du métaspace dépend de la complexité de votre code, par exemple le nombre de classes.

La taille directe de la mémoire dépend de votre débit et de votre utilisation de bibliothèques tierces telles que nio et gzip.

La liste suivante décrit un exemple de disposition de mémoire classique pour les applications de 2 Go. Vous pouvez faire référence à cette liste pour configurer vos paramètres de taille de mémoire.

  • Mémoire totale (2 048M)
  • Mémoire de segment : Xmx est de 1 433,6M (70 % de la mémoire totale). La valeur de référence de l’utilisation quotidienne de la mémoire est de 1 200M.
    • Jeune génération
      • Espace survivant (S0, S1)
      • Espace Eden
    • Ancienne génération
  • Mémoire non segmentée
    • Partie observée (observée par Spring Boot Actuator)
      • Métaspace : la valeur de référence de l’utilisation quotidienne est de 50M-256M
      • Cache de code
      • Espace de classe compressé
    • Partie non observée (non observée par Spring Boot Actuator) : la valeur de référence de l’utilisation quotidienne est de 150M-250M.
      • Pile de threads
      • GC, symbole interne et autres
  • Mémoire directe : la valeur de référence de l’utilisation quotidienne est de 10M-200M.

Le diagramme suivant présente les mêmes informations. Les nombres en gris sont les valeurs de référence de l’utilisation quotidienne de la mémoire.

Diagramme de sortie de mémoire standard pour les applications 2-Go.

Dans l’ensemble, lors de la configuration des tailles de mémoire maximales, vous devez envisager l’utilisation de chaque partie en mémoire, et la somme de toutes les tailles maximales ne doit pas dépasser le total de la mémoire disponible.

Java OOM

OOM signifie que l’application est hors mémoire. Il existe deux concepts différents : conteneur OOM et JVM OOM. Pour plus d’informations, consultez Problèmes de redémarrage d’application provoqués par des problèmes hors mémoire.

Voir aussi