Perché ottengo “insufficiente archiviazione è disponibile per elaborare questo comando” utilizzando Java MappedByteBuffers?

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

  •  20-09-2019
  •  | 
  •  

Domanda

Ho una grande serie di doppie che sto usando un file basato su disco e una paginazione Elenco dei MappedByteBuffers per gestire, vedere questa domanda per sfondo più. Sono in esecuzione su Windows XP utilizzando Java 1.5.

Questa è la parte fondamentale del mio codice che fa l'allocazione dei buffer contro il file ...

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;
} 

ottengo l'errore indicato nel titolo dopo aver allocare il secondo o terzo tampone.

ho pensato che fosse qualcosa a che fare con la memoria contigua disponibile, così ho provato con diverse dimensioni e il numero di pagine, ma senza alcun beneficio complessivo.

Che cosa significa esattamente "insufficiente archiviazione è disponibile per elaborare questo comando" significa e che cosa, se non altro, posso fare?

Ho pensato che il punto di MappedByteBuffers è stata la capacità di essere in grado di gestire strutture più grandi di quanto si possa montare sul mucchio, e trattarli come se fossero in memoria.

Degli indizi?

Modifica

In risposta ad una risposta qui sotto (@adsk) ho cambiato il mio codice in modo non ho mai hanno più di un singolo MappedByteBuffer attiva alla volta. Quando mi riferisco ad una regione del file che è attualmente mappata ho spazzatura mappa esistente e crearne uno nuovo. Ho ancora lo stesso errore dopo circa 3 operazioni della mappa.

Il bug citato con GC non raccogliere i MappedByteBuffers sembra essere ancora un problema in JDK 1.5.

È stato utile?

Soluzione

  

Ho pensato che il punto di MappedByteBuffers è stata la capacità di essere in grado di gestire strutture più grandi di quanto si possa montare sul mucchio, e trattarli come se fossero in memoria.

No. L'idea è / era di permettere di indirizzare più di a 2 ** 31 doppie ... sul presupposto che hai avuto abbastanza memoria, e sono stati utilizzando un 64 bit JVM.

(io parto dal presupposto che si tratta di una domanda followup a questa domanda .)

Modifica :. Chiaramente, sono necessarie ulteriori spiegazioni

Ci sono una serie di limiti che entrano in gioco.

  1. Java ha una restrizione fondamentale che l'attributo length di una matrice, e indici di matrice hanno tipo int. Questo, combinato con il fatto che int firmato un array non può avere una dimensione negativo significa che il più grande allineamento possibile può avere elementi 2**31. Questa restrizione si applica a 32bit e 64bit JVM. Si tratta di una parte fondamentale del linguaggio Java ... come il fatto che i valori vanno da char 0 a 65535.

  2. Utilizzando una JVM 32bit pone un (teorica) del limite superiore 2**32 il numero di byte che sono indirizzabili dalla JVM. Questo include, l'intero mucchio, il codice e le classi di libreria che si utilizza, nucleo codice nativo della JVM, memoria usata per i buffer mappati ... tutto. (In realtà, a seconda della piattaforma, il sistema operativo possono dare a molto meno di byte 2**32 se lo spazio di indirizzi.)

  3. I parametri che si danno sulla riga di comando java determinare la quantità di memoria heap JVM sarà permettere l'applicazione da utilizzare. Memoria mappata utilizzando oggetti MappedByteBuffer non conta in questa direzione.

  4. La quantità di memoria che il sistema operativo vi darà dipende (su Linux / UNIX) sulla quantità totale di spazio di swap configurata, i limiti di 'processo' e così via. limiti simili probabilmente si applicano a Windows. E, naturalmente, è possibile eseguire solo una JVM a 64 bit se il sistema operativo host è a 64 bit in grado, e che si sta utilizzando 64bit hardware in grado. (Se si dispone di un processore Pentium, si sono semplicemente fuori di fortuna.)

  5. Infine, la quantità di memoria fisica nel sistema entra in gioco. In teoria, potete chiedere al vostro JVM di utilizzare un mucchio, ecc che è molte volte più grande di quello della memoria fisica del dispositivo. In pratica, si tratta di una cattiva idea . Se avete più di allocare la memoria virtuale, il sistema si thrash e le prestazioni delle applicazioni passerà attraverso il pavimento.

Il take away è questa:

  • Se si utilizza un 32 bit JVM, probabilmente sono limitati a qualche parte tra 2**31 e 2**32 byte di memoria indirizzabile. Questo è sufficiente spazio per un massimo compreso tra 2**29 e 2**30 doppie, se si utilizza un array o un buffer mappato.

  • Se si utilizza un 64 bit JVM, è possibile rappresentare un singolo array di doppie 2**31. Il limite teorico di un buffer mappato sarebbe byte o doppie 2**63 2**61, ma il limite pratico porterebbe più o meno la quantità di memoria fisica della macchina ha.

Altri suggerimenti

Quando la memoria la mappatura di un file, è possibile esaurire lo spazio indirizzo a 32-bit VM. Ciò avviene anche se il file è mappato in piccoli pezzi e quelle ByteBuffers non sono più raggiungibili. La ragione è che GC mai calci per liberare i buffer a.

consultare il bug a http://bugs.sun.com/bugdatabase /view_bug.do?bug_id=6417205

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top