Écoconception d'un service Java : ce que le RGESN demande au backend, et ce que la JVM permet de mesurer
Depuis mai 2024, la France dispose d'un référentiel public pour l'écoconception des services numériques, le RGESN. Il est surtout commenté pour ses critères d'interface : poids des pages, vidéos, captation de l'attention. Pourtant plusieurs critères visent directement le serveur, et une application Java bien ou mal réglée peut y faire une vraie différence. Voici ce que le référentiel demande, et comment la JVM permet d'y répondre avec des mesures plutôt qu'avec des intentions.
Le cadre : loi REEN et RGESN
L'article 25 de la loi n° 2021-1485 du 15 novembre 2021 visant à réduire l'empreinte environnementale du numérique en France (loi « REEN ») a confié à l'Arcep et à l'Arcom, en lien avec l'ADEME, la définition d'un référentiel général de l'écoconception des services numériques. La version 2024 a été présentée le 17 mai 2024 ; la DINUM, la CNIL et Inria y ont contribué. Elle compte 78 critères, formulés comme des questions, répartis en neuf thèmes : stratégie, spécifications, architecture, expérience et interface utilisateur, contenus, frontend, backend, hébergement et algorithmie. Le référentiel et ses fiches pratiques sont publiés sur le site de la mission Numérique écoresponsable.
Deux précisions du document lui-même sont importantes. D'abord, il est non contraignant : aucune administration ni entreprise n'est obligée de l'appliquer. Ensuite, quiconque veut s'en prévaloir doit publier une déclaration d'écoconception datée, et toute communication qui s'y réfère doit respecter les règles sur les allégations environnementales (fiabilité, transparence, vérifiabilité). Autrement dit : si vous écrivez que votre service Java est écoconçu, il faut pouvoir le montrer.
Le référentiel rappelle aussi l'enjeu en citant l'étude de l'ADEME et de l'Arcep : le numérique représente 2,5 % de l'empreinte carbone de la France, et ce sont les terminaux, surtout leur fabrication, qui en représentent la majeure partie. Le backend n'est donc pas le premier poste ; il reste celui sur lequel une équipe d'exploitation a la main.
Les critères qui touchent un backend Java
- 7.1 Cache serveur : « Le service numérique a-t-il recours à un système de cache serveur pour les données les plus utilisées ? » La fiche demande d'identifier les données à mettre en cache, de fixer une durée d'expiration et un mécanisme de rafraîchissement, puis d'expliquer la stratégie dans la déclaration.
- 7.2 Durées de conservation : des dates d'expiration sur les données et un processus, de préférence automatique, d'archivage ou de suppression. La fiche fait le lien avec le RGPD.
- 7.3 Traitements en arrière-plan : informer l'utilisateur d'un traitement en cours.
- 8.1 à 8.10 Hébergement : démarche environnementale de l'hébergeur, PUE et WUE, origine de l'électricité, localisation, données « chaudes » et « froides » hébergées séparément (8.8), duplication limitée au nécessaire (8.9), calculs asynchrones planifiés en tenant compte des contraintes externes (8.10).
Aucun critère ne cite la JVM, le ramasse-miettes ou la taille du tas. Mais chacun suppose de savoir ce que consomme le service : un cache serveur trop gros pour le tas déclenche des collections à répétition, et un service surdimensionné occupe des serveurs pour rien. C'est là que les outils du JDK servent.
Mesurer avant de déclarer
La mémoire réellement utilisée
La première question est l'écart entre la mémoire réservée et la mémoire utile. Dans un conteneur, la JVM détecte la limite mémoire (UseContainerSupport, activé par défaut) et, sans réglage explicite, fixe le tas maximal à 25 % de cette limite (MaxRAMPercentage). Vérifiez la valeur effective plutôt que de la supposer :
java -XX:+PrintFlagsFinal -version | grep -E 'MaxHeapSize|MaxRAMPercentage'
jcmd <pid> GC.heap_info
jcmd <pid> VM.native_memory summary # si lancé avec -XX:NativeMemoryTracking=summary
Native Memory Tracking montre ce qui s'ajoute au tas : Metaspace, threads, code compilé, tampons. Notre guide en anglais sur Native Memory Tracking explique comment lire ce rapport.
Le CPU passé à collecter
Le journal unifié du ramasse-miettes (-Xlog:gc*:file=gc.log:time,uptime) indique la durée et la fréquence des pauses. Un service qui passe une part notable de son temps à collecter consomme du CPU sans rien produire : c'est souvent le signe d'un tas trop petit pour son cache ou d'une allocation excessive. Le guide Reading GC logs détaille la lecture.
Le profil sur la durée
JDK Flight Recorder enregistre, avec un surcoût faible, la charge CPU de la JVM (jdk.CPULoad), le taux d'allocation et les pauses. Un enregistrement continu sur une journée type donne la base chiffrée d'une déclaration d'écoconception : consommation moyenne, pics, et effet d'un changement de réglage.
jcmd <pid> JFR.start name=base settings=default maxage=24h
jcmd <pid> JFR.dump name=base filename=/var/diag/base.jfr
Réduire sans dégrader
Rendre la mémoire inutilisée
Une JVM garde volontiers la mémoire qu'elle a obtenue lors d'un pic. Avec G1, le collecteur par défaut, l'option G1PeriodicGCInterval (JEP 346, JDK 12), exprimée en millisecondes et désactivée par défaut (0), déclenche des collections périodiques quand l'application est peu active, afin de rendre la mémoire inutilisée au système. ZGC rend la mémoire inutilisée d'office (JEP 351, JDK 13), après un délai réglé par ZUncommitDelay, 300 secondes par défaut. Sur un hôte qui mutualise plusieurs services, c'est de la mémoire disponible pour les autres.
Démarrer plus vite
Pour des services souvent redémarrés ou mis à l'échelle automatiquement, le partage de données de classes (CDS) réduit le travail de démarrage. Depuis le JDK 13 (JEP 350), -XX:ArchiveClassesAtExit=app.jsa crée une archive à la fin d'une exécution d'entraînement, réutilisée ensuite avec -XX:SharedArchiveFile=app.jsa. Mesurez le gain sur votre application : il dépend du nombre de classes chargées.
Dimensionner le cache selon le tas
Le critère 7.1 encourage le cache serveur ; la JVM impose de le borner. Un cache sans limite de taille ni expiration finit en OutOfMemoryError: Java heap space, ou en collections incessantes. Fixez une taille maximale exprimée en nombre d'entrées ou en octets, une durée d'expiration conforme à la fiche du critère, et suivez le taux de succès du cache : un cache peu consulté consomme de la mémoire sans rien économiser.
Tenir les durées de conservation
Le critère 7.2 vaut aussi pour les fichiers que produit la JVM : journaux GC, enregistrements JFR, heap dumps. La rotation des journaux (-Xlog:gc*:file=gc.log::filecount=5,filesize=20m) et une purge des fichiers de diagnostic évitent qu'ils s'accumulent. Pour les heap dumps, qui contiennent des données personnelles, voir notre article Heap dump et RGPD.
Ce qu'il faut écrire dans la déclaration
Une déclaration honnête pour le backend tient en quelques éléments vérifiables : la stratégie de cache (quoi, quelle taille, quelle expiration), les durées de conservation des données et des fichiers techniques, l'hébergement retenu avec les indicateurs que l'hébergeur publie réellement, et les mesures de consommation obtenues avec les outils ci-dessus, datées et accompagnées de la version du JDK. Évitez les formules du type « service sobre » sans chiffre : le référentiel lui-même rappelle que les allégations environnementales doivent être vérifiables.
Pour situer la place de Java dans le code publié par les administrations, voir notre analyse de l'inventaire code.gouv.fr.
Sources
- Arcep, Arcom, « Référentiel général de l'écoconception des services numériques », version 2024 (PDF publié par la mission Numérique écoresponsable), notamment le contexte, la partie « Déclaration d'écoconception » et les critères 7.1 à 8.10
- Arcep, communiqué du 17 mai 2024 sur la publication du référentiel
- OpenJDK : JEP 346, JEP 350 et JEP 351 ; référence de la commande
java(JDK 21) pourMaxRAMPercentage,G1PeriodicGCIntervaletZUncommitDelay