Comment convaincre Java Garbage Collector de s'exécuter lorsque l'ensemble de travail est petit?


Ceci est encore une autre question "veuillez me dire comment forcer le garbage collector Java à s'exécuter". Dans notre demande, je crois que nous avons de bonnes raisons de le faire.

Il s'agit d'une application serveur, qui contient généralement environ 5 millions d'objets en direct. Une fois toutes les 5 minutes, nous effectuons une tâche d'analyse qui prend ~60 secondes. Si un GC complet est déclenché pendant l'exécution de l'analyse, il y aura environ 40 millions d'objets en direct. Les objets supplémentaires 35M deviennent des ordures lorsque l'analyse est terminée. Le le serveur doit rester réactif aux demandes à tout moment (même pendant l'exécution de l'analyse).

Nous avons constaté qu'un GC complet prend environ 1,5 seconde s'il est invoqué lorsque l'analyse n'est pas en cours d'exécution, mais environ 15 secondes pendant l'exécution de l'analyse. Malheureusement, notre modèle d'allocation est tel que le GCs complet se déclenche généralement pendant l'analyse, même si l'analyse ne s'exécute que 20% du temps. (Chaque troisième ou quatrième exécution d'analyse déclenche un GC complet.)

J'ai ajouté du code à appeler le Système tant méprisé.gc () juste avant de commencer une analyse, si l'espace libre dans l'ancienne génération est inférieur à un certain seuil (5 Go). L'avantage était très important: nous obtenons des temps de pause de 1,5 seconde au lieu de 15 secondes, et nous libérons plus de déchets dans le marché. Cependant, parfois le système.l'appel gc () est ignoré, et nous nous retrouvons avec une pause de 15 secondes quelques minutes plus tard lorsque le GC est déclenché automatiquement.

Ma question, alors: y a-t-il quelque chose que nous pouvons faire pour convaincre plus fortement le garbage collector de s'exécuter? Nous exécutons 1.7.0_09-icedtea et utilisons le GC parallèle. Je voudrais soit (a) un moyen fiable de forcer manuellement la collecte des ordures, soit (b) un moyen de régler le collecteur afin qu'il prenne une décision automatique plus intelligente. (b) semble difficile, car il n'est pas clair pour moi comment le collectionneur pourrait détecter que notre ensemble de travail varie de cette manière dramatique.

Je suis prêt à recourir à un piratage substantiel si nécessaire; c'est un problème sérieux pour nous. (Nous pourrions examiner les compacteurs CMS ou G1 comme alternatives, mais je me méfie de l'impact du débit du CMS, et G1 est réputé se comporter mal face aux grands tableaux d'octets, que nous utilisons.)

Addendum: En production, notre expérience jusqu'à présent a été de ce Système.gc () habituellement, déclenche une récupération de place complète; au moins, dans les situations où nous l'appelons. (Nous ne l'appelons qu'une fois toutes les 10 à 30 minutes, avec le tas un peu mais pas complètement rempli d'ordures.) Ce serait bien de pouvoir déclencher la collecte des ordures de manière plus fiable, mais cela nous aide la plupart du temps.

Author: Steve, 2013-10-17

4 answers

Votre problème est que vous exécutez deux applications avec des exigences et des profils de mémoire entièrement différents dans la même JVM.

Exécutez l'analyse des données séparément, dans un processus non orienté vers l'utilisateur, de sorte que le serveur orienté vers l'utilisateur reste constamment réactif. Je suppose que l'analyse périodique génère un résumé ou des données de résultat d'une sorte quelconque; rendez-le disponible pour les utilisateurs finaux en l'expédiant sur le serveur orienté utilisateur afin qu'il puisse être servi à partir de là, ou laissez votre front-end aller chercher séparément à partir de l'analyse de serveur.

 5
Author: DPM, 2013-10-17 01:05:56

Envisagez d'utiliser de la mémoire non gérée, c'est-à-dire ByteBuffers à la place des tableaux d'octets.

Je ne peux offrir qu'un hack qui aura besoin d'un réglage et qui pourrait ou non fonctionner. J'essaierais d'abord les solutions les plus saines. Lorsque vous voulez forcer le GC, faites-le en allouant beaucoup de mémoire. Faites ceci pour que la mémoire puisse être immédiatement récupérée, mais pour que toute l'allocation ne puisse pas être optimisée (quelque chose comme sum += new byte[123456].hashCode() devrait faire). Vous devrez trouver une méthode fiable pour déterminer quand arrêter. Un objet avec un finaliseur pourrait vous le dire ou peut-être regarder runtime.getFreeMemory pourrait vous aider.

 2
Author: maaartinus, 2013-10-17 01:21:58

Plutôt que de répondre directement à votre question (je ne peux pas), j'aimerais offrir une alternative possible.

Il semble que vous allouiez un grand nombre de grands tableaux d'octets pendant votre exécution d'analyse, puis que vous leur permettiez d'être ramassés à la fin de l'exécution (ou que vous essayiez de les forcer à être ramassés juste avant la prochaine exécution).

Au Lieu de cela, si possible, essayez de gérer votre propre piscine de tableaux d'octets, de sorte que, dans le meilleur des cas, vous allouer tous les tableaux une fois que l'application est démarrée, puis ils vivent pour la durée de vie de l'application, et n'ont pas besoin de déchets collectés.

Cette idée peut bien sûr être étendue à des structures de données et des instances d'objets plus complexes.

Tout cela est un peu plus de travail que d'allouer de la mémoire lorsque vous en avez besoin, et de la "libérer" lorsque vous ne le faites pas, mais devrait réduire considérablement le travail que le garbage collector doit faire.

 1
Author: GreyBeardedGeek, 2013-10-17 00:57:16

J'ai trouvé que java GC traite très mal un grand nombre d'objets (objets 20-100m). votre situation aurait été pire si ces objets étaient restés en vie, car GC serait horrible même s'il n'y avait rien à collecter.

La solution consiste à réduire le nombre d'objets (pas la mémoire totale que vous utilisez). J'oserais deviner que votre phase d'analyse utilise des collections et de nombreux wrappers primitifs (Entier, long, etc.). si c'est le cas, un la solution consiste à passer à une bibliothèque de collections primitives. une telle bibliothèque est ce que j'ai créé pour résoudre un problème similaire que j'ai rencontré où j'ai exécuté une simulation avec des objets vivants 100m pendant une longue période. Cette bibliothèque s'appelle Banana , voir le wiki pour plus de détails.

 -1
Author: Omry Yadan, 2013-10-17 16:17:44