Utilisation de la mémoire virtuelle depuis Java sous Linux, trop de mémoire utilisée

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

quand je lance l'application, en utilisant la taille 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 certains problèmes avec d'autres logiciels sur l'ordinateur, qui est relativement limité en ressources.

la mémoire virtuelle réservée ne sera pas utilisée de toute façon, pour autant que je comprenne, parce que une fois que nous avons atteint la limite de tas, un OutOfMemoryError est lancé. J'ai lancé la même application sous windows et je vois que la taille de la mémoire virtuelle et la taille du tas sont similaires.

y a-t-il de toute façon que je puisse configurer la mémoire virtuelle utilisée pour un processus Java sous Linux?

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

Edit 2 : L'utilisation de ulimit -v permet de limiter la quantité de mémoire virtuelle. Si la taille définie est inférieure à 204 Mo, alors l'application ne fonctionnera pas même si elle n'a pas besoin de 204 Mo, seulement 64 Mo. Je veux donc comprendre pourquoi Java nécessite autant de mémoire virtuelle. Cela peut-il être changé?

Edit 3 : il existe 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).

225
demandé sur Lii 2009-02-18 17:24:48

8 réponses

cela a été une plainte de longue date avec Java, mais il est largement sans signification, et généralement basé sur l'examen de la mauvaise information. Le phrasé habituel est quelque chose comme " Hello World on Java takes 10 megabytes! Pourquoi est-il besoin de ça?"Eh bien, voici un moyen de faire Hello World sur un 64-bit JVM revendication de prendre plus de 4 gigaoctets ... au moins par une forme de mesure.

java -Xms1024m -Xmx4096m com.example.Hello

Différentes Manières de Mesurer la Mémoire

sur Linux, le top commande vous donne plusieurs nombres différents pour la mémoire. Voici ce qu'il dit à propos de L'exemple de Hello World:

  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 mémoire virtuel: la somme de tout dans la carte mémoire virtuelle (voir ci-dessous). Il est largement dénué de sens, sauf quand il ne l'est pas (voir ci-dessous).
  • RES est la taille de l'ensemble résident: le nombre de pages qui résident actuellement en RAM. Dans presque tous les cas, c'est le le seul nombre que vous devez utiliser en disant "trop grand."Mais ce n'est toujours pas un très bon nombre, 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é aux bibliothèques partagées et aux fichiers Jarf mappés en mémoire. Dans cet exemple, je n'ai eu qu'un seul processus Java, donc je soupçonne que le 7k est le résultat de bibliothèques utilisées par le système D'exploitation.
  • SWAP n'est pas activé par par défaut, et qui n'est pas montré ici. Il indique la quantité de mémoire virtuelle qui réside actuellement sur le disque, que ce soit ou non dans l'espace de pagination . L'OS est très bon pour garder les pages actives en RAM, et les seuls remèdes pour l'échange 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ée. Sous Windows XP, il y a des colonnes" usage de la mémoire "et" taille de la mémoire virtuelle", mais la documentation officielle est muette sur ce qu'elles signifient. Windows Vista et Windows 7 ajoutent plus de colonnes, et ils sont en fait documentée . Parmi celles-ci, la mesure" Working Set " est la plus utile; elle correspond grosso modo à la somme de RES 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 de Java), mais aussi toutes les bibliothèques partagées et les fichiers mémoire utilisés par le programme. Sur Linux, vous pouvez utiliser la commande pmap pour voir tout ce qui est cartographié dans l'espace de processus (à partir de Maintenant, je vais seulement me référer à Linux, parce que c'est ce que j'utilise; je suis sûr qu'il y a des outils équivalents pour Windows). Voici un extrait de la carte mémoire du " Hello World" programme; la carte mémoire entière est plus de 100 lignes de long, et il n'est pas inhabituel 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 avec l'adresse mémoire virtuelle du segment. Ceci est suivi par la taille du segment, les permissions, et la source du segment. Ce dernier élément est un fichier ou "anon", qui indique un bloc de mémoire alloué par mmap .

à partir du en haut, nous avons

  • La JVM chargeur (c'est à dire, le programme qui est exécuté lorsque vous tapez java ). C'est très petit; tout ce qu'il fait c'est charger dans les bibliothèques partagées où le vrai code JVM est stocké.
  • un tas de blocs anon contenant le tas Java et les données internes. Il s'agit d'un Sun JVM, de sorte que le tas est divisé en plusieurs générations, dont chacune est son propre bloc de mémoire. Notez que la JVM alloue de l'espace mémoire virtuel basé sur Valeur -Xmx ; cela lui permet d'avoir un tas contigu. La valeur -Xms est utilisée à l'interne pour dire combien de tas est" utilisé " lorsque le programme démarre, et pour déclencher la collecte des ordures lorsque cette limite est approchée.
  • un fichier JARfile cartographié en mémoire, dans ce cas le fichier qui contient les classes "JDK"."Lorsque vous faites la carte mémoire d'un bocal, vous pouvez y accéder très efficacement (au lieu de le lire dès le début). Le Sun JVM va tout mémoriser JARs sur le classpath; si votre code d'application a besoin d'accéder à un JAR, vous pouvez aussi le mapper en mémoire.
  • données par thread pour deux threads. Le bloc de 1m est une pile de threads; Je ne sais pas ce qui va dans le bloc de 4K. Pour une vraie application, vous verrez des douzaines sinon des centaines de ces entrées répétées à travers la carte mémoire.
  • une des bibliothèques partagées qui détient le code JVM réel. Il ya plusieurs de ces.
  • la bibliothèque partagée pour la bibliothèque standard C. C'est juste une des nombreuses choses que la JVM charge qui ne font 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 en lecture-écriture qui contient des données globales par processus pour la bibliothèque (Je ne sais pas ce qu'est le segment sans permissions; Je ne l'ai vu que sur x64 Linux). La partie en lecture seule du la bibliothèque peut être partagée entre tous les processus qui utilisent la bibliothèque; par exemple, libc a 1,5 M d'espace mémoire virtuel qui peut être partagé.

quand la taille de la mémoire virtuelle est-elle importante?

la carte mémoire virtuelle contient beaucoup de choses. Une partie est en lecture seule, une partie est partagée, et une partie est attribuée mais jamais touchée (par exemple, presque tous les 4 Go de tas dans cet exemple). Mais le système d'exploitation est assez intelligent pour ne charge que ce qu'il besoins, de sorte que la taille de la mémoire virtuelle est largement hors de propos.

où la taille de la mémoire virtuelle est importante est si vous utilisez un système d'exploitation 32 bits, où vous ne pouvez allouer que 2Go (ou, dans certains cas, 3Go) d'espace d'adresse de processus. Dans ce cas, vous avez affaire à une ressource rare, et pourrait avoir à faire des compromis, tels que la réduction de la taille de votre tas afin de la mémoire-map Un grand fichier ou de créer beaucoup de threads.

mais, étant donné que les machines 64 bits sont omniprésents, Je ne pense pas qu'il faudra longtemps avant que la taille de la mémoire virtuelle est une statistique complètement hors de propos.

quand est-ce que la taille de L'ensemble des Résidents est importante?

Taille de L'ensemble résident est la partie de l'espace de mémoire virtuelle qui est réellement en RAM. Si votre RSS devient une partie importante de votre mémoire physique totale, il pourrait être temps de commencer à s'inquiéter. Si votre RSS prend de l'ampleur pour prendre en charge toute votre mémoire physique, et que votre système commence à échanger, c'est bien passé le temps de commencer à s'inquiéter.

mais RSS est aussi trompeur, surtout sur une machine légèrement chargée. Le système d'exploitation n'est pas dépenser beaucoup d'efforts pour récupérer les pages utilisées par un processus. Il n'y a que peu d'avantages à en tirer et il est possible qu'une page soit entachée d'une erreur coûteuse si le processus touche la page à l'avenir. En conséquence, la statistique RSS peut inclure beaucoup de pages qui ne sont pas en usage actif.

à moins que vous ne changiez, ne vous inquiétez pas trop de ce que les diverses statistiques de mémoire vous disent. Avec la mise en garde qu'un RSS de plus en plus grand peut indiquer une sorte de fuite de mémoire.

avec un programme Java, il est beaucoup plus important de faire attention à ce qui se passe dans le tas. La quantité totale d'espace consommé est importante, et il y a certaines étapes que vous pouvez prendre pour réduire cela. Le plus important est la quantité de temps que vous passez dans la collecte des ordures, et quelles parties du tas sont recueillies.

accéder au disque (une base de données) coûte cher, et la mémoire est bon marché. Si vous pouvez échanger l'un pour l'autre, le faire.

564
répondu kdgregory 2016-01-05 02:01:28

il y a un problème connu avec Java et glibc >= 2.10 (inclut Ubuntu >= 10.04, RHEL >= 6).

le remède est de mettre cet env. variable: export MALLOC_ARENA_MAX=4 Si vous utilisez Tomcat, vous pouvez l'ajouter au fichier TOMCAT_HOME/bin/setenv.sh .

il y a un article D'IBM sur la configuration de MALLOC_ARENA_MAX https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en

ce billet de blog dit

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

utilisez MALLOC_ARENA_MAX sur Google pour obtenir plus de références.

vous pourriez vouloir accorder aussi d'autres options 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
33
répondu Lari Hotari 2015-04-21 20:13:17

la quantité de mémoire allouée pour le processus Java est à peu près égale à ce à quoi je m'attendais. J'ai eu des problèmes similaires à exécuter Java sur des systèmes embarqués/limités en mémoire. Exécuter n'importe quelle application avec des limites de VM arbitraires ou sur des systèmes qui n'ont pas des quantités adéquates de swap ont tendance à se casser. Il semble être la nature de nombreuses applications modernes qui ne sont pas la conception pour une utilisation sur les systèmes de ressources limitées.

vous avez encore quelques options que vous pouvez essayez de limiter l'empreinte mémoire de votre JVM. Cela pourrait réduire l'empreinte mémoire virtuelle:

- XX: ReservedCodeCacheSize=32m Taille du cache de code réservé ( en octets) - maximum Taille du cache de code. [Solaris 64 bits, amd64, et serveur x86: 48m; dans 1.5.0_06 et avant, Solaris 64-bit et and64: 1024m.]

- XX: MaxPermSize=64m Taille de la génération permanente. [5.0 et versions plus récentes: Les Vm 64 bits ont une échelle de 30% supérieure; 1,4 amd64: 96m; 1.3.1-client: 32m.]

de plus, vous devez également définir votre-Xmx (taille Max heap) à une valeur aussi proche que possible de la utilisation réelle de la mémoire de pointe de votre application. Je crois que le comportement par défaut de la JVM est encore de double la taille de tas chaque fois qu'elle l'étend jusqu'au max. Si vous commencez avec 32M tas et votre application a atteint 65M, alors le tas finirait par croître 32M - > 64M - > 128M.

vous pourriez aussi essayer de rendre la VM moins agressive au sujet de la croissance du tas:

-XX:MinHeapFreeRatio=40 pourcentage Minimum de mémoire libre après GC à évitez l'expansion.

aussi, d'après ce que je me rappelle d'avoir expérimenté avec cela il y a quelques années, le nombre de bibliothèques natives chargées a eu un impact énorme sur l'empreinte minimale. Chargement java.net.Socket ajouté plus de 15M si je me souviens correctement (et probablement pas moi).

9
répondu James Schek 2009-02-18 16:21:52

le Sun JVM nécessite beaucoup de mémoire pour les hotspots et les cartes it dans les bibliothèques d'exécution en mémoire partagée.

si la mémoire est un problème, envisagez d'utiliser une autre MVC appropriée pour l'intégration. IBM a j9, et il y a l'Open Source "jamvm" qui utilise les bibliothèques GNU classpath. Sun a aussi la JVM Squeak qui tourne sur les taches solaires, donc il y a des alternatives.

7
répondu Thorbjørn Ravn Andersen 2009-03-03 17:14:16

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

ce n'est pas une solution réelle car il limiterait l'espace d'adresse Disponible pour tous processus, mais cela vous permettrait de vérifier le comportement de votre application avec une mémoire virtuelle limitée.

2
répondu VonC 2009-02-18 14:51:39

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

EDIT: pour définir de petites valeurs pour-XX: MaxHeapFreeRatio vous devez également définir - XX: MinHeapFreeRatio Eg

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

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

Default parameters

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

MinHeap 10, MaxHeap 25

à la fin, le tas max est 722, le tas utilisé est 378

cela a en fait un certain inpact, car notre application tourne sur un serveur de bureau distant, et de nombreux utilisateurs peuvent l'exécuter à la fois.

2
répondu runholen 2015-01-22 07:35:40

java 1.4 de Sun a les arguments suivants pour contrôler la taille de la mémoire:

- Xmsn Spécifiez la taille initiale, en octets, du pool d'allocation de mémoire. Cette valeur doit être un multiple de 1024 plus de 1 MO. Ajouter la lettre k ou K pour indiquer kilooctets, ou m ou M pour indiquer mégaoctets. Défaut la valeur est de 2MB. Exemples:

           -Xms6291456
           -Xms6144k
           -Xms6m

- Xmxn Spécifiez la taille maximale, en octets, de la mémoire répartition de la piscine. Cette valeur doit être un multiple de 1024 plus de 2 MO. Ajouter la lettre k ou K pour indiquer kilooctets, ou m ou M pour indiquer mégaoctets. Défaut la valeur est de 64 Mo. 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

1
répondu Paul Tomblin 2009-02-18 14:35:14

Non, vous ne pouvez pas configurer la quantité de mémoire requise par VM. Cependant, notez qu'il s'agit d'une mémoire virtuelle, qui n'est pas résidente, donc elle y reste sans danger si elle n'est pas utilisée.

Alernativement, vous pouvez essayer une autre JVM puis Sun one, avec une plus petite empreinte mémoire, mais je ne peux pas vous conseiller ici.

0
répondu Marko 2009-02-18 14:28:52