Pourquoi suis-je « Pas assez de stockage est disponible pour traiter cette commande » en utilisant Java MappedByteBuffers?

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

  •  20-09-2019
  •  | 
  •  

Question

J'ai un très large éventail de doubles que j'utilise un fichier sur disque et une liste d'échange de MappedByteBuffers à manipuler, voir cette question pour plus d'arrière-plan. Je suis en cours d'exécution sur Windows XP en utilisant Java 1.5.

Voici la partie clé de mon code qui fait l'attribution des tampons contre le fichier ...

try 
{
 // create a random access file and size it so it can hold all our data = the extent x the size of a double
 f = new File(_base_filename);
 _filename = f.getAbsolutePath();
 _ioFile = new RandomAccessFile(f, "rw");
 _ioFile.setLength(_extent * BLOCK_SIZE);
    _ioChannel = _ioFile.getChannel();

    // make enough MappedByteBuffers to handle the whole lot
 _pagesize = bytes_extent;
 long pages = 1;
 long diff = 0;
 while (_pagesize > MAX_PAGE_SIZE)
 {
  _pagesize  /= PAGE_DIVISION;
  pages *= PAGE_DIVISION;

  // make sure we are at double boundaries.  We cannot have a double spanning pages
  diff = _pagesize  % BLOCK_SIZE;
  if (diff != 0) _pagesize  -= diff;

 }

 // what is the difference between the total bytes associated with all the pages and the
 // total overall bytes?  There is a good chance we'll have a few left over because of the
 // rounding down that happens when the page size is halved
 diff = bytes_extent - (_pagesize  * pages);
 if (diff > 0)
 {
  // check whether adding on the remainder to the last page will tip it over the max size
  // if not then we just need to allocate the remainder to the final page
  if (_pagesize  + diff > MAX_PAGE_SIZE)
  {
   // need one more page
   pages++;
  }
 }

 // make the byte buffers and put them on the list
 int size = (int) _pagesize ;  // safe cast because of the loop which drops maxsize below Integer.MAX_INT
 int offset = 0;
 for (int page = 0; page < pages; page++)
 {
  offset = (int) (page * _pagesize );

  // the last page should be just big enough to accommodate any left over odd bytes
  if ((bytes_extent - offset) < _pagesize )
  {
   size = (int) (bytes_extent - offset);
  }

  // map the buffer to the right place 
     MappedByteBuffer buf = _ioChannel.map(FileChannel.MapMode.READ_WRITE, offset, size);

     // stick the buffer on the list
     _bufs.add(buf);
 }

 Controller.g_Logger.info("Created memory map file :" + _filename);
 Controller.g_Logger.info("Using " + _bufs.size() + " MappedByteBuffers");
    _ioChannel.close();
    _ioFile.close(); 
} 
catch (Exception e) 
{
 Controller.g_Logger.error("Error opening memory map file: " + _base_filename);
 Controller.g_Logger.error("Error creating memory map file: " + e.getMessage());
 e.printStackTrace();
 Clear();
    if (_ioChannel != null) _ioChannel.close();
    if (_ioFile != null) _ioFile.close();
 if (f != null) f.delete();
 throw e;
} 

Je reçois l'erreur mentionnée dans le titre après que je le tampon allouer deuxième ou troisième.

Je pensais que c'était quelque chose à voir avec la mémoire contigu disponible, ont donc essayé avec différentes tailles et le nombre de pages, mais sans bénéfice global.

Que signifie exactement « Pas assez de stockage est disponible pour traiter cette commande » signifie et que, si quelque chose, puis-je faire?

Je pensais que le point de MappedByteBuffers était la capacité d'être en mesure de gérer des structures plus grandes que vous pourriez tenir sur le tas, et les traiter comme si elles étaient en mémoire.

Les indices?

EDIT:

En réponse à une réponse ci-dessous (@adsk) J'ai changé mon code, donc je ne jamais avoir plus d'un seul MappedByteBuffer actif à un moment donné. Quand je me réfère à une région du fichier qui est actuellement unmapped je JUNK la carte existante et de créer une nouvelle. Je reçois toujours la même erreur après environ 3 opérations de carte.

Le bug cité par GC ne recueille pas les MappedByteBuffers semble encore être un problème dans JDK 1.5.

Était-ce utile?

La solution

  

Je pensais que le point de MappedByteBuffers était la capacité d'être en mesure de gérer des structures plus grandes que vous pourriez tenir sur le tas, et les traiter comme si elles étaient en mémoire.

Non. L'idée est / était de vous permettre d'adresser plus de 2 ** 31 doubles ... en supposant que vous aviez assez de mémoire, et utilisaient une machine virtuelle Java 64 bits.

(je suppose que cela est une question de suivi cette question.)

EDIT :. De toute évidence, plus d'explications est nécessaire

Il y a un certain nombre de limites qui entrent en jeu.

  1. Java a une restriction fondamentale que l'attribut length d'un tableau, et les indices de tableau ont le type int. Ceci, combiné avec le fait que int est signé et un tableau ne peut pas avoir une taille négative signifie que le plus grand éventail possible peut avoir des éléments de 2**31. Cette restriction s'applique à JVMs 32bits et 64bits. Il est un élément fondamental du langage Java ... comme le fait que les valeurs de char vont de 0 à 65535.

  2. En utilisant une machine virtuelle Java 32 bits PLACES un (théorique) limite supérieure de 2**32 sur le nombre d'octets qui sont adressables par la machine virtuelle Java. Cela comprend, tout le tas, votre code, et les classes de bibliothèque que vous utilisez, le noyau de code natif de la machine virtuelle Java, la mémoire utilisée pour les tampons cartographiés ... tout. (En fait, en fonction de votre plate-forme, le système d'exploitation peut vous donner beaucoup moins que les octets de 2**32 si l'espace d'adressage.)

  3. Les paramètres que vous donnez sur la ligne de commande java déterminent la quantité de mémoire tas JVM allow votre application à utiliser. La mémoire mappée à l'aide d'objets MappedByteBuffer ne compte pas dans ce sens.

  4. La quantité de mémoire que le système d'exploitation vous donnera dépend (sous Linux / UNIX) sur la quantité totale d'espace d'échange configuré, les limites de « processus » et ainsi de suite. Des limites similaires s'appliquent probablement à Windows. Et bien sûr, vous ne pouvez exécuter une machine virtuelle Java 64 bits si le système d'exploitation hôte est capable 64bit, et que vous utilisez 64bit matériel capable. (Si vous avez un Pentium, vous êtes simplement hors de la chance.)

  5. Enfin, la quantité de mémoire physique de votre système entre en jeu. En théorie, vous pouvez demander à votre machine virtuelle Java d'utiliser un tas, etc qui est plusieurs fois plus grand que que la mémoire physique de votre machine. En pratique, cela est une mauvaise idée . Si vous allouez sur la mémoire virtuelle, votre système thrash et les performances des applications passera par le sol.

L'emporter est la suivante:

  • Si vous utilisez une machine virtuelle Java 32 bits, vous êtes probablement limité à quelque part entre 2**31 et 2**32 octets de mémoire adressable. C'est assez d'espace pour un maximum entre doubles de 2**29 et 2**30, si vous utilisez un tableau ou un tampon mappé.

  • Si vous utilisez une machine virtuelle Java 64 bits, vous pouvez représenter un seul tableau de doubles de 2**31. La limite théorique d'un tampon cartographié serait 2**63 octets ou doubles de 2**61, mais la limite pratique serait à peu près la quantité de mémoire physique de votre machine a.

Autres conseils

Lorsque la mémoire en correspondance d'un fichier, il est possible de manquer d'espace d'adresse dans VM 32-bit. Cela se produit même si le fichier est mis en correspondance en petits morceaux et les ByteBuffers ne sont plus accessibles. La raison est que GC ne donne le coup pour libérer les tampons.

Reportez-vous le bogue http://bugs.sun.com/bugdatabase /view_bug.do?bug_id=6417205

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