Pourquoi ai-je une erreur OutOfMemoryError lors de l'insertion de 50 000 objets dans HashMap?

StackOverflow https://stackoverflow.com/questions/235047

  •  04-07-2019
  •  | 
  •  

Question

J'essaie d'insérer environ 50 000 objets (et donc 50 000 clés) dans un java.util.HashMap<java.awt.Point, Segment>. Cependant, je continue à avoir une exception OutOfMemory. (Segment est ma propre classe - poids très léger - un String champ et 3 int champs).

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.resize(HashMap.java:508)
    at java.util.HashMap.addEntry(HashMap.java:799)
    at java.util.HashMap.put(HashMap.java:431)
    at bus.tools.UpdateMap.putSegment(UpdateMap.java:168)

Cela semble tout à fait ridicule, car je constate qu'il y a beaucoup de mémoire disponible sur la machine - à la fois en RAM libre et en espace HD pour la mémoire virtuelle.

Est-il possible que Java fonctionne avec des exigences de mémoire strictes? Puis-je les augmenter?

Existe-t-il une limite étrange avec HashMap? Est-ce que je vais devoir mettre en place le mien? Existe-t-il d’autres cours intéressants?

(J'utilise Java 5 sous OS X 10.5 sur un ordinateur Intel doté de 2 Go de RAM.)

Était-ce utile?

La solution

Vous pouvez augmenter la taille de segment maximale en passant -Xmx128m (où 128 correspond au nombre de mégaoctets) à java. Je ne me souviens pas de la taille par défaut, mais il me semble que c'était quelque chose d'assez petit.

Vous pouvez vérifier par programme la quantité de mémoire disponible en utilisant Classe d'exécution .

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

(Exemple tiré de Almanach des développeurs Java )

Ceci est également partiellement traité dans la Forum aux questions sur la machine virtuelle Java HotSpot . et sur la page Réglage de Java 6 GC .

Autres conseils

Certaines personnes suggèrent de modifier les paramètres de HashMap pour renforcer les besoins en mémoire. Je suggérerais de mesurer au lieu de deviner ; ce pourrait être quelque chose d'autre causant l'OOME. En particulier, je suggérerais d'utiliser NetBeans Profiler ou VisualVM (fourni avec Java 6, mais je vois que vous êtes coincé avec Java 5).

Une autre chose à essayer si vous connaissez le nombre d’objets à l’avance est d’utiliser le constructeur HashMap (int capacity, double loadfactor) à la place du constructeur no-arg par défaut qui utilise les valeurs par défaut de (16,0.75). Si le nombre d'éléments dans votre HashMap est supérieur à (capacité * loadfactor), le tableau sous-jacent dans HashMap sera redimensionné à la puissance 2 suivante et la table sera réorganisée. Ce tableau nécessite également une zone de mémoire contiguë. Par exemple, si vous doublez un tableau de la taille 32768 à 65536, vous aurez besoin d’un bloc de 256 Ko de mémoire libre. Pour éviter l’allocation supplémentaire et les pénalités de remise en jeu, utilisez d’emblée une table de hachage plus grande. Cela diminuera également le risque de ne pas avoir une zone de mémoire contiguë suffisamment grande pour s’adapter à la carte.

Les implémentations sont généralement supportées par des tableaux. Les tableaux sont des blocs de mémoire de taille fixe. L’implémentation de la table de hachage commence par le stockage de données dans l’un de ces tableaux à une capacité donnée, par exemple 100 objets.

S'il remplit le tableau et que vous continuez à ajouter des objets, la carte doit augmenter secrètement sa taille. Puisque les tableaux sont fixes, il crée un tableau entièrement nouveau, en mémoire, avec le tableau actuel, qui est légèrement plus grand. Ceci est appelé croissance du tableau. Ensuite, tous les éléments de l’ancien tableau sont copiés dans le nouveau tableau et celui-ci est déréférencé avec l’espoir qu’il sera récupéré et la mémoire libérée à un moment donné.

Généralement, le code qui augmente la capacité de la carte en copiant des éléments dans un tableau plus grand est à l'origine d'un tel problème. Il y a & "; Bête &"; les implémentations et les solutions intelligentes qui utilisent un facteur de croissance ou de charge qui détermine la taille du nouveau tableau en fonction de la taille de l'ancien tableau. Certaines implémentations cachent ces paramètres et d'autres non, vous ne pouvez donc pas toujours les définir. Le problème est que, lorsque vous ne pouvez pas le définir, il choisit un facteur de charge par défaut, tel que 2. Ainsi, le nouveau tableau est deux fois plus grand que l'ancien. Maintenant, votre carte supposée 50k a un tableau de sauvegarde de 100k.

Vérifiez si vous pouvez réduire le facteur de charge à 0,25 ou quelque chose du genre. cela entraîne davantage de collisions entre les cartes de hachage, ce qui nuit aux performances, mais vous rencontrez un goulot d'étranglement dans la mémoire et vous devez le faire.

Utilisez ce constructeur:

( http: // java.sun.com/javase/6/docs/api/java/util/HashMap.html#HashMap(int , float))

Vous devez probablement définir l'indicateur -Xmx512m ou un nombre plus important lors du démarrage de Java. Je pense que 64 Mo est la valeur par défaut.

Édité pour ajouter: Une fois que vous avez déterminé la quantité de mémoire que vos objets utilisent réellement avec un profileur, vous pouvez vous pencher sur les références faibles ou les références molles pour vous assurer que vous ne gardez pas accidentellement une partie de votre mémoire dans le ramasse-miettes lorsque vous n'êtes pas occupé. plus les utiliser.

Peut-être aussi envie de jeter un coup d'oeil à ceci:

http://java.sun.com/docs/hotspot/gc/

Dans ces réponses, il est implicite que Java a une taille fixe pour la mémoire et ne dépasse pas la taille de segment maximale configurée. C’est différent de C, par exemple, où il n’est contraint que par la machine sur laquelle il est exécuté.

Par défaut, la machine virtuelle Java utilise un espace de mémoire limité. La limite dépend de l'implémentation de la machine virtuelle Java et le type de machine virtuelle que vous utilisez n'est pas clair. Sur les systèmes d'exploitation autres que Windows, une JVM Sun 32 bits sur une machine de 2 Go ou plus utilisera une taille de pile maximale par défaut de 1/4 de la mémoire physique, ou de 512 Mo dans votre cas. Cependant, la valeur par défaut pour un & Quot; client & Quot; mode JVM n’a que 64 Mo de taille maximale de tas, ce qui peut être ce que vous avez rencontré. Les machines virtuelles d'autres fournisseurs peuvent choisir des valeurs par défaut différentes.

Bien entendu, vous pouvez spécifier explicitement la limite de segment de mémoire avec l'option -Xmx<NN>m sur java, où <NN> correspond au nombre de mégaoctets du segment de mémoire.

En gros, votre table de hachage ne devrait utiliser que 16 Mo environ. Il doit donc y avoir d'autres objets volumineux sur le tas. Si vous pouviez utiliser une Comparable clé dans un TreeMap, vous économiseriez de la mémoire.

Voir & "Ergonomie dans la JVM 5.0 &"; pour plus de détails.

L'espace de mémoire Java est limité par défaut, mais cela semble toujours extrême (quelle est la taille de vos 50000 segments?)

Je soupçonne que vous avez un autre problème, tel que les tableaux de l'ensemble deviennent trop grands car tout est assigné dans le même & "; emplacement &"; (affecte également la performance, bien sûr). Cela semble toutefois peu probable si vos points sont répartis uniformément.

Je me demande cependant pourquoi vous utilisez un HashMap plutôt qu'un TreeMap? Même si les points sont bidimensionnels, vous pouvez les sous-classer avec une fonction de comparaison, puis effectuer des recherches log (n).

Pensée aléatoire: les compartiments de hachage associés à HashMap ne sont pas particulièrement efficaces en termes de mémoire. Vous voudrez peut-être essayer TreeMap comme alternative et voir s'il offre toujours des performances suffisantes.

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