Il est vraiment difficile d'utiliser efficacement java. De nombreux pools sont illimités et la JVM échoue de façon catastrophique lorsqu'une tentative d'allocation échoue. Toute la mémoire n'est pas réellement engagée, mais une grande partie est réservée donc en comptant vers la limite de mémoire virtuelle imposée par ulimit. Après de nombreuses recherches, j'ai découvert plusieurs des différentes catégories d'utilisation de la mémoire Java. Cette réponse s'applique à OpenJDK et Oracle 8.x sur un système 64 bits:
Tas
Ceci est la partie la plus bien comprise de la mémoire JVM. C'est là que la majorité de la mémoire de votre programme est utilisée. Il peut être contrôlé avec les options -Xmx
et -Xms
.
Metaspace
Cela semble tenir des métadonnées sur les classes qui ont été chargés. Je n'ai pas pu savoir si cette catégorie libérerait de la mémoire sur le système d'exploitation, ou si elle ne ferait que croître. Le maximum par défaut semble être 1g. Il peut être contrôlé avec l'option -XX:MaxMetaspaceSize
. Remarque: la spécification de ceci ne peut rien faire sans spécifier également l'espace de classe compressé .
espace classe comprimé
Cela semble lié à la Metaspace. Je n'ai pas pu savoir si cette catégorie libérerait de la mémoire sur le système d'exploitation, ou si elle ne ferait que croître. Le maximum par défaut semble être 1g. Il peut être contrôlé avec l'option '-XX: CompressedClassSpaceSize`.
Garbage collector en tête
Il semble y avoir un montant fixe des frais généraux en fonction du garbage collector sélectionné, ainsi qu'une allocation supplémentaire en fonction de la taille du tas. L'observation suggère que cette surcharge représente environ 5% de la taille du tas. Il n'y a pas d'options connues pour limiter cela (autre que de sélectionner un algorithme GC différent).
Fils
Chaque __gVirt_NP_NNS_NNPS<__ réserves de fil 1m pour sa pile. La JVM semble réserver 50m de mémoire supplémentaire en tant que mesure de sécurité contre les débordements de pile. La taille de la pile peut être contrôlée avec l'option -Xss
. La taille de sécurité ne peut pas être contrôlée. Comme il n'existe aucun moyen d'imposer un nombre maximal de threads et que chaque thread requiert une certaine quantité de mémoire, ce pool de mémoire est techniquement illimité.
fichiers Jar (et fichiers zip)
La mise en œuvre zip par défaut utiliser le mappage de mémoire pour l'accès aux fichiers zip. Cela signifie que chaque fichier jar et fichier zip accédé sera mappé en mémoire (nécessitant une quantité de mémoire réservée égale à la somme des tailles de fichier). Ce comportement peut être désactivé en définissant la propriété du système sun.zip.disableMemoryMapping
(comme dans -Dsun.zip.disableMemoryMapping=true
)
NIO buffers directs
Tout tampon directe (en utilisant allocateDirect
créé à) utilisera cette quantité de mémoire hors tas. La meilleure performance de NIO vient avec des tampons directs, donc beaucoup de frameworks les utiliseront. La JVM ne permet aucunement de limiter la quantité totale de mémoire allouée aux buffers NIO. Ce pool est donc techniquement illimité.
En outre, cette mémoire est dupliquée sur le tas pour chaque thread qui touche le tampon. Voir this pour plus de détails.
mémoire natif alloué par les bibliothèques
Si vous utilisez des bibliothèques natives, une mémoire qu'ils allouent sera hors tas. Certaines bibliothèques Java de base (telles que java.util.zip.ZipFile
) utilisent également des bibliothèques natives qui consomment de la mémoire. La JVM ne permet pas de limiter la quantité totale de mémoire allouée par les bibliothèques natives, ce pool est techniquement illimité.
arènes malloc
La machine virtuelle Java utilise malloc pour un grand nombre de ces demandes de mémoire natifs. Pour éviter les problèmes de conflit de thread, la fonction malloc utilise plusieurs pools pré-alloués. Le nombre par défaut de pools est égal à 8 x cpu mais peut être remplacé en définissant la variable d'environnement MALLOC_ARENAS_MAX
. Chaque pool réserve une certaine quantité de mémoire même si tout n'est pas utilisé. Le paramètre MALLOC_ARENAS_MAX
à 1-4 est généralement recommandé pour java, car les affectations les plus fréquentes sont effectuées à partir du tas, et un nombre d'arènes inférieur empêche la mémoire virtuelle gaspillée de compter vers l'ulimit.
Cette catégorie n'est pas techniquement son propre pool, mais elle explique l'allocation virtuelle de la mémoire supplémentaire.
Il manque de mémoire et est tué par 'cgroups', ou manque de mémoire et lance' OutOfMemoryError'? – EJP
Java se termine par un 'OutOfMemoryError'. J'ai mis à jour la question. – tombrown52
Donc, il fait ce que vous voulez. Tout ce que vous avez à faire est de trouver des arguments de mémoire Java réalisables qui correspondent à votre limite 'cgroups'. S'il n'y en a pas, vous ne pouvez pas le faire. – EJP