2017-05-31 1 views
2

Mon programme Java doit s'exécuter dans un environnement où la mémoire est limitée à une quantité spécifiée. Lorsque j'exécute mon service Java, il manque de mémoire au démarrage.Comment utiliser ulimit avec java correctement?

Voici un exemple des commandes que je utilise des valeurs et je réglage:

ulimit -Sv 1500000 
java \ 
    -Xmx1000m -Xms1000m \ 
    -XX:MaxMetaspaceSize=500m \ 
    -XX:CompressedClassSpaceSize=500m \ 
    -XX:+ExitOnOutOfMemoryError \ 
    MyClass 

En théorie, je suis pour tout ce que je RÉFÉRENCIÉS pouvais trouver de la documentation sur. Il y a le tas (1000m) et le métaspace (500m). Mais il manque encore de mémoire au démarrage lors de l'initialisation de la JVM. Cela arrive quand je place l'ulimit d'environ 600mib plus grand que le tas + métaspace.

De quelle catégorie de mémoire ai-je besoin pour pouvoir définir ulimit de manière appropriée? Cas d'utilisation: J'exécute une tâche dans un conteneur Docker avec une mémoire limitée. Cela signifie que les cgroups Linux font la limitation. Lorsque les limites de mémoire sont dépassées, les groupes de contrôle ne peuvent que suspendre ou supprimer le processus qui dépasse ses limites. Je veux vraiment que le processus Java échoue gracieusement si quelque chose ne va pas et qu'il utilise trop de mémoire pour que le script bash de retour puisse signaler l'erreur à l'initiateur de la tâche. Nous utilisons Java 8 donc nous devons nous soucier de méta-espace au lieu de permgen.

Mise à jour: Il ne meurt pas avec un . C'est l'erreur:

Error occurred during initialization of VM 
Could not allocate metaspace: 524288000 bytes 
+0

Il manque de mémoire et est tué par 'cgroups', ou manque de mémoire et lance' OutOfMemoryError'? – EJP

+0

Java se termine par un 'OutOfMemoryError'. J'ai mis à jour la question. – tombrown52

+0

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

Répondre

1

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.