Question

J'ai un problème avec une application Java sous Linux.

Lorsque je lance l'application, en utilisant la taille de segment maximale par défaut (64 Mo), je vois en utilisant l'application tops que 240 Mo de mémoire virtuelle sont alloués à l'application. Cela crée quelques problèmes avec un autre logiciel sur l'ordinateur, ce qui est relativement aux ressources limitées.

La mémoire virtuelle réservée ne sera pas utilisé de toute façon, pour autant que je comprends, car une fois que nous atteignons le tas limite un OutOfMemoryError est jeté. J'ai couru la même application sous Windows et je vois que la taille de la mémoire virtuelle et la taille Heap sont similaires.

Y at-il de toute façon que je peux configurer la mémoire virtuelle utilisée pour un processus Java sous Linux?

Modifier 1 : Le problème n'est pas le Heap. Le problème est que si je mets un tas de 128 Mo, par exemple, encore Linux alloue 210 Mo de mémoire virtuelle, qui est pas nécessaire, jamais. **

Edit 2 : Utilisation de ulimit -v permet de limiter la quantité de mémoire virtuelle. Si le jeu de la taille est inférieure à 204 Mo, l'application ne fonctionnera pas, même si elle n'a pas besoin de 204 Mo, seulement 64 MB. Donc, je veux comprendre pourquoi Java nécessite tant la mémoire virtuelle. Cela peut-il être modifié?

Modifier 3 : Il y a plusieurs autres applications en cours d'exécution dans le système, qui est intégré. Et le système a une limite de mémoire virtuelle (des commentaires, détail important).

Était-ce utile?

La solution

Cela a été une plainte de longue date avec Java, mais il est en grande partie vide de sens, et généralement basée sur la recherche à des informations erronées. Le phrasé habituel est quelque chose comme « Bonjour tout le monde sur Java prend 10 méga-octets! Pourquoi at-il besoin que? » Eh bien, voici un moyen de faire Bonjour tout le monde sur une allégation de JVM 64 bits pour prendre plus de 4 gigaoctets ... au moins par une forme de mesure.

java -Xms1024m -Xmx4096m com.example.Hello

Différentes façons de mesurer la mémoire

Sous Linux, la commande top vous donne plusieurs numéros différents pour la mémoire. Voici ce qu'il dit à propos de l'exemple Bonjour tout le monde:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 2120 kgregory  20   0 4373m  15m 7152 S    0  0.2   0:00.10 java
  • VIRT est l'espace de mémoire virtuelle: la somme de tout sur la carte de mémoire virtuelle (voir ci-dessous). Il est en grande partie dénuée de sens, sauf quand il est pas (voir ci-dessous).
  • RES est la taille de l'ensemble des résidents: le nombre de pages qui résident actuellement dans la RAM. Dans presque tous les cas, c'est le seul numéro que vous devez utiliser pour dire « trop grand. » Mais il est toujours pas un très bon, surtout quand on parle de Java.
  • SHR est la quantité de mémoire résidente qui est partagée avec d'autres processus. Pour un processus Java, cela est généralement limitée à des bibliothèques partagées et JARfiles mappés en mémoire. Dans cet exemple, je n'avais qu'un seul processus Java en cours, donc je pense que le 7k est le résultat des bibliothèques utilisées par le système d'exploitation.
  • SWAP n'est pas activé par défaut, et ne figure pas ici. Il indique la quantité de mémoire virtuelle qui est actuellement résident sur le disque, si oui ou non il est en fait dans l'espace de swap . Le système d'exploitation est très bon de garder les pages actives dans la mémoire vive, et les seuls remèdes pour la permutation sont (1) acheter plus de mémoire, ou (2) réduire le nombre de processus, il est donc préférable d'ignorer ce nombre.

La situation pour Windows Task Manager est un peu plus compliqué. Sous Windows XP, il y a des colonnes « Utilisation de la mémoire » et « Taille de la mémoire virtuelle », mais le documentation officielle est muet sur ce qu'ils signifient. Windows Vista et Windows 7 ajouter d'autres colonnes, et ils sont en fait documenté . Parmi ceux-ci, la mesure « Set de travail » est le plus utile; il correspond approximativement à la somme des SER et SHR sur Linux.

Comprendre la carte mémoire virtuelle

La mémoire virtuelle consommée par un processus est le total de tout ce qui est dans la carte mémoire du processus. Cela inclut les données (par exemple, le tas Java), mais aussi toutes les bibliothèques partagées et les fichiers mappés en mémoire utilisés par le programme. Sous Linux, vous pouvez utiliser la commande pmap pour voir toutes les choses cartographiées dans le processus l'espace (à partir de maintenant je vais seulement faire référence à Linux, car il est ce que je l'utilise, je suis sûr qu'il ya des outils équivalents pour Windows). Voici un extrait de la carte mémoire du programme « Bonjour tout le monde »; toute carte mémoire est plus de 100 lignes, et il est pas rare d'avoir une liste de mille lignes.

0000000040000000     36K r-x--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000      8K rwx--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000    676K rwx--    [ anon ]
00000006fae00000  21248K rwx--    [ anon ]
00000006fc2c0000  62720K rwx--    [ anon ]
0000000700000000 699072K rwx--    [ anon ]
000000072aab0000 2097152K rwx--    [ anon ]
00000007aaab0000 349504K rwx--    [ anon ]
00000007c0000000 1048576K rwx--    [ anon ]
...
00007fa1ed00d000   1652K r-xs-  /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000   1024K rwx--    [ anon ]
00007fa1ed2d3000      4K -----    [ anon ]
00007fa1ed2d4000   1024K rwx--    [ anon ]
00007fa1ed3d4000      4K -----    [ anon ]
...
00007fa1f20d3000    164K r-x--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000   1020K -----  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000     28K rwx--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000   1576K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000     16K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000      4K rwx--  /lib/x86_64-linux-gnu/libc-2.13.so
...

Une explication rapide du format: chaque ligne commence par l'adresse de la mémoire virtuelle du segment. Ceci est suivi par la taille du segment, les autorisations, et la source du segment. Ce dernier élément est un fichier ou « Anon », ce qui indique un bloc de mémoire allouée par mmap .

En partant du haut, nous avons

  • Le chargeur JVM (à savoir le programme soit lancé lorsque vous tapez java). Ceci est très petit; tout ce qu'il fait est la charge dans les bibliothèques partagées où le vrai code machine virtuelle Java est stocké.
  • Un groupe de blocs Anon tenant le tas Java et les données internes. Ceci est une machine virtuelle Java Sun, de sorte que le tas est bRoken en plusieurs générations, dont chacun est son propre bloc de mémoire. Notez que la machine virtuelle Java alloue de l'espace de mémoire virtuelle basée sur la valeur de -Xmx; Cela lui permet d'avoir un tas contigu. La valeur -Xms est utilisé en interne pour dire combien de tas est « utilisé » lorsque le programme démarre, et pour déclencher la collecte des ordures que cette limite est abordée.
  • Un JarFile mémoire mappée, dans ce cas, le fichier qui contient les « classes JDK. » Lorsque vous mappez un mémoire JAR, vous pouvez accéder aux fichiers dans très efficacement (par rapport à la lecture chaque fois depuis le début). La machine virtuelle Java Sun carte mémoire tous les fichiers JAR sur le chemin de classe; si votre code d'application doit accéder à un fichier JAR, vous pouvez également mémoire mapper.
  • données par thread pour deux threads. Le bloc 1 M est une pile de fil; Je ne sais pas ce qui se passe dans le bloc de 4 Ko. Pour une application réelle, vous verrez des dizaines, voire des centaines de ces entrées répétées dans la carte mémoire.
  • L'une des bibliothèques partagées qui contient le code de la JVM réelle. Il y a plusieurs.
  • La bibliothèque partagée pour la bibliothèque standard C. Ceci est juste une des nombreuses choses que les charges JVM qui ne sont pas strictement partie de Java.

Les bibliothèques partagées sont particulièrement intéressantes: chaque bibliothèque partagée a au moins deux segments: un segment en lecture seule contenant le code de la bibliothèque, et un segment de lecture-écriture qui contient des données globales par processus pour la bibliothèque (je ne sais pas savoir ce qui est le segment sans autorisations, je ne l'ai vu sur Linux x64). La partie en lecture seule de la bibliothèque peut être partagée entre tous les processus qui utilisent la bibliothèque; par exemple, libc a 1.5M de l'espace de mémoire virtuelle qui peut être partagée.

Quand la mémoire virtuelle Taille importante?

La carte mémoire virtuelle contient beaucoup de choses. Certaines d'entre elles sont en lecture seule, certaines d'entre elles sont partagées, et certaines d'entre elles sont alloués, mais jamais touché (par exemple, la quasi-totalité du 4Gb de tas dans cet exemple). Mais le système d'exploitation est assez intelligent pour charger uniquement ce dont il a besoin, donc la taille de la mémoire virtuelle est largement hors de propos.

Si la taille de la mémoire virtuelle est important est que si vous êtes en cours d'exécution sur un système d'exploitation 32 bits, vous ne pouvez affecter 2Gb (ou, dans certains cas, 3Go) de l'espace d'adressage du processus. Dans ce cas, vous avez affaire à une ressource rare, et peut-être à faire des compromis, tels que la réduction de votre taille de tas pour mémoire mapper un gros fichier ou créer beaucoup de threads.

Mais, étant donné que les machines 64 bits sont omniprésentes, je ne pense pas que ce sera bien avant que la taille de la mémoire virtuelle est une statistique tout à fait hors de propos.

Quand est résident Régler la taille importante?

Resident taille Set est la partie de l'espace de mémoire virtuelle qui est en fait dans la RAM. Si votre flux pousse à être une partie importante de votre mémoire physique totale, il pourrait être temps de commencer à se soucier. Si votre RSS pousse à prendre toute votre mémoire physique, et votre système commence l'échange, il est temps passé pour bien commencer à se soucier.

Mais RSS est également trompeur, en particulier sur une machine peu chargée. Le système d'exploitation ne dépense pas beaucoup d'efforts pour récupérer les pages utilisées par un processus. Il y a peu d'avantages à gagner en le faisant, et le potentiel d'une défaillance de page coûteuse si le processus touche la page à l'avenir. En conséquence, la statistique de RSS peut inclure beaucoup de pages qui ne sont pas en cours d'utilisation active.

Bottom Line

À moins que vous permutant, ne soyez pas trop préoccupé par ce que les différentes statistiques de mémoire vous disent. Avec la mise en garde qu'un RSS sans cesse croissante peut indiquer une sorte de fuite de mémoire.

Avec un programme Java, il est beaucoup plus important de prêter attention à ce qui se passe dans le tas. La quantité totale d'espace consommé est important, et il y a quelques mesures que vous pouvez prendre pour réduire cela. Plus important est la quantité de temps que vous passez dans la collecte des ordures, et quelles parties du tas obtiennent collected.

Accès au disque (par exemple une base de données) est cher, et la mémoire est pas cher. Si vous pouvez échanger un pour l'autre, faites-le.

Autres conseils

Il y a un problème connu avec Java et glibc> = 2.10 (comprend Ubuntu> = 10,04, RHEL> = 6).

Le remède est de mettre ce env. variable:

export MALLOC_ARENA_MAX=4

Si vous utilisez Tomcat, vous pouvez ajouter à fichier TOMCAT_HOME/bin/setenv.sh.

Pour Docker, ajoutez ceci à Dockerfile

ENV MALLOC_ARENA_MAX=4

Il y a un article sur la configuration IBM MALLOC_ARENA_MAX https://www.ibm.com/developerworks/ communauté / blogs / kevgrig / entrée / linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage? lang = fr

Ce blog dit

  

mémoire résidente a été connu au fluage d'une manière similaire à un   fuite de mémoire ou la fragmentation de la mémoire.

Il y a aussi un bug ouvert -8193521 JDK « déchets glibc mémoire avec la configuration par défaut «

recherche sur Google ou MALLOC_ARENA_MAX SO plus de références.

Vous pouvez affiner aussi d'autres options de malloc pour optimiser pour une faible fragmentation de la mémoire allouée:

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536

La quantité de mémoire allouée pour le processus Java est à peu près sur pied d'égalité avec ce que j'attendais. J'ai eu des problèmes similaires en cours d'exécution Java sur les systèmes limités embedded / mémoire. Exécution toute application avec des limites arbitraires VM ou sur des systèmes qui ne disposent pas de quantités suffisantes d'échange ont tendance à se briser. Il semble être la nature de nombreuses applications modernes qui ne sont pas la conception pour les systèmes à ressources limitées.

Vous avez quelques options que vous pouvez essayer de limiter votre empreinte mémoire de machine virtuelle Java. Cela pourrait réduire l'empreinte mémoire virtuelle:

  

-XX: ReservedCodeCacheSize = 32m code réservé taille du cache (en octets) - maximale   Code taille du cache. [Solaris 64 bits,   amd64 et -server x86: 48m; dans   1.5.0_06 et plus tôt, 64 bits Solaris et and64:. 1024m]

     

-XX: MaxPermSize = 64m Taille de la génération permanente. [5.0 et plus récent:   64 machines virtuelles de bits sont mis à l'échelle 30% plus grand; 1.4   amd64: 96m; 1.3.1 -client:. 32m]

En outre, vous devriez également définir votre -Xmx (taille de tas max) à une valeur aussi proche que possible du utilisation de la mémoire de crête réelle de votre application. Je crois que le comportement par défaut de la machine virtuelle Java est encore à à double la taille du tas chaque fois qu'il l'étend au max. Si vous commencez avec tas de 32M et votre application a atteint un sommet à 65M, puis le tas finirait par 32M de plus en plus -> 64M -.> 128M

Vous pouvez également essayer pour rendre la machine virtuelle moins agressif au sujet de plus en plus le tas:

  

-XX: MinHeapFreeRatio = 40 Pourcentage minimal de tas libre après GC à   éviter l'expansion.

En outre, d'après ce que je me souviens d'expérimenter avec cela il y a quelques années, le nombre de bibliothèques natives chargé a un impact énorme sur l'encombrement minimum. Chargement java.net.Socket ajouté plus de 15M si je me souviens bien (et je ne doute pas).

La machine virtuelle Java Sun nécessite beaucoup de mémoire pour HotSpot et cartes dans les bibliothèques d'exécution en mémoire partagée.

Si la mémoire est un problème envisager d'utiliser une autre machine virtuelle adaptée pour l'intégration. IBM a J9, et il y a l'Open Source « jamvm » qui utilise les bibliothèques GNU Classpathx. Aussi Sun a la machine virtuelle Java Squeak fonctionne sur les taches solaires donc il existe des alternatives.

Juste une pensée, mais vous pouvez vérifier l'influence de une option de ulimit -v .

Ce n'est pas une solution réelle puisqu'elle espace d'adressage limite disponible pour tous processus, mais qui vous permettra de vérifier le comportement de votre application avec une mémoire virtuelle limitée.

Une façon de réduire le SICE tas d'un système avec des ressources limitées peut être de jouer avec la -XX: Variable MaxHeapFreeRatio. Cela est généralement fixé à 70, et le pourcentage maximum du tas qui est libre avant que le GC se rétrécit il. Réglage à une valeur inférieure, et vous verrez par exemple dans le profileur jvisualvm qu'un petit tas SICE est généralement utilisé pour votre programme.

EDIT: Pour définir les petites valeurs pour -XX: MaxHeapFreeRatio vous devez également définir -XX: MinHeapFreeRatio Par exemple,

java -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=25 HelloWorld

EDIT2: Ajout d'un exemple pour une application réelle qui commence et fait la même tâche, l'un avec les paramètres par défaut et une avec 10 et 25 en tant que paramètres. Je ne l'ai pas remarqué de différence de vitesse, bien que java devrait en théorie utiliser plus de temps pour augmenter le tas dans le dernier exemple.

Paramètres par défaut

A la fin, tas max est 905, tas est utilisé 378

MinHeap 10, MaxHeap 25

A la fin, tas max est 722, tas est utilisé 378

ont fait quelque Inpact, notre application est exécutée sur un serveur de bureau à distance, et de nombreux utilisateurs peuvent exécuter à la fois.

La Java de Sun 1.4 présente les arguments ci-après pour contrôler la taille de la mémoire:

  

-Xmsn       Spécifier la taille initiale, en octets, de la piscine d'allocation de mémoire.   Cette valeur doit être un multiple de 1024   supérieure à 1MB. Annexer la lettre k   ou K pour indiquer kilo-octets, ou m ou M   pour indiquer méga-octets. Le défaut   La valeur est 2Mo. Exemples:

           -Xms6291456
           -Xms6144k
           -Xms6m
     

-Xmxn       Indiquez la taille maximale, en octets, du pool d'allocation de mémoire.   Cette valeur doit un multiple de 1024   supérieure à 2Mo. Annexer la lettre k   ou K pour indiquer kilo-octets, ou m ou M   pour indiquer méga-octets. Le défaut   La valeur est 64Mo. Exemples:

           -Xmx83886080
           -Xmx81920k
           -Xmx80m

http://java.sun.com /j2se/1.4.2/docs/tooldocs/windows/java.html

Java 5 et 6 ont un peu plus. Voir http://java.sun.com/javase/technologies/hotspot/vmoptions .jsp

Non, vous ne pouvez pas configurer quantité de mémoire nécessaire par VM. Toutefois, notez que cette mémoire virtuelle, ne réside pas, il reste juste là sans dommage sinon utilisé.

Alernatively, vous pouvez essayer une autre machine virtuelle Java puis un Sun, avec une empreinte mémoire plus petite, mais je ne peux pas conseiller ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top