Warum erhalte ich „nicht genügend Speicher verfügbar, um diesen Befehl zu verarbeiten“ mit Hilfe von Java MappedByteBuffers?

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

  •  20-09-2019
  •  | 
  •  

Frage

Ich habe eine sehr große Auswahl an Doppeln, dass ich eine Disk-basierte Datei verwenden und eine Paging-Liste der MappedByteBuffers zu handhaben, finden Sie unter diese Frage für mehr Hintergrund. Ich bin auf Windows XP mit Hilfe von Java 1.5.

Hier ist der Schlüssel Teil meiner Code, der die Zuordnung der Puffer gegen die Datei wird ...

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

ich den Fehler im Titel erwähnt erhalten, nachdem ich den zweiten oder dritten Puffer zuzuweisen.

Ich dachte, es war etwas mit zusammenhängendem Speicher zur Verfügung, dies zu tun versucht hat, es mit verschiedenen Größen und Anzahl der Seiten, aber insgesamt keinen Nutzen.

Was genau funktioniert „nicht genügend Speicher verfügbar, um diesen Befehl zu verarbeiten“ Mittelwert und was, wenn überhaupt, dann kann ich dagegen tun?

Ich dachte, der Punkt des MappedByteBuffers war die Fähigkeit der Lage sein, Strukturen zu behandeln größer als man auf dem Heap passen könnte, und behandeln sie, als ob sie in Erinnerung waren.

Irgendwelche Hinweise?

EDIT:

Als Reaktion auf eine Antwort unten (@adsk) änderte ich meinen Code so dass ich nie zu einem beliebigen Zeitpunkt mehr als ein einziger aktiver MappedByteBuffer habe. Wenn ich beziehe mich auf einen Bereich der Datei, die derzeit nicht zugeordnete I Junk die vorhandene Karte und eine neue erstellen. Ich bekomme immer noch den gleichen Fehler nach etwa 3 Map-Operationen.

Der Bug zitierte mit GC nicht den MappedByteBuffers zu sammeln scheint immer noch ein Problem in JDK 1.5 zu sein.

War es hilfreich?

Lösung

  

Ich dachte, der Punkt des MappedByteBuffers war die Fähigkeit der Lage sein, Strukturen zu behandeln größer als man auf dem Heap passen könnte, und behandeln sie, als ob sie in Erinnerung waren.

Nein. Die Idee ist / war Sie Adresse, damit mehr als 2 ** 31 Doppelzimmer ... auf der Annahme, dass Sie genügend Gedächtnis hatten, und wurden unter Verwendung eines 64-Bit-JVM.

(Ich gehe davon aus, dass dies eine Followup Frage auf diese Frage .)

Bearbeiten . Offensichtlich mehr Erklärung benötigt wird,

Es gibt eine Reihe von Grenzen, die ins Spiel kommen.

  1. Java hat eine fundamentale Einschränkung, dass das length Attribut eines Arrays und Array-Indizes Typ int haben. Dies, kombiniert mit der Tatsache, dass int signiert ist und ein Array kann keine negative Größe Mittel aufweisen, dass die größte mögliche Array 2**31 Elemente haben können. Diese Einschränkung gilt für 32-Bit- und 64-Bit-JVMs. Es ist ein grundlegender Bestandteil der Java-Sprache ... wie die Tatsache, dass char Werte von 0 zu 65535 gehen.

  2. Mit einer 32-Bit-JVM legt eine (theoretische) Ober von 2**32 auf der Anzahl von Bytes gebunden, die von der JVM adressierbar sind. Dazu gehören die gesamte Halde, Code und Bibliotheksklassen, die Sie verwenden, den nativen Code Kern JVM, Speicher für abgebildet Puffer verwendet ... alles. (In der Tat, je nach Plattform, die OS können geben Sie deutlich weniger als 2**32 Bytes, wenn Adressraum).

  3. Die Parameter, die Sie auf der Java-Befehlszeile geben bestimmen, wie viel Heap-Speicher JVM erlauben Ihre Anwendung zu verwenden. Speicher gemappt MappedByteBuffer Objekte verwenden, nicht in diese Richtung zählen.

  4. Die Speichermenge, die das Betriebssystem geben Ihnen hängt (unter Linux / UNIX) auf die Gesamtmenge des Swap-Speicher konfiguriert, der ‚Prozess‘ Grenzen und so weiter. Ähnliche Grenzen wahrscheinlich auf Windows anzuwenden. Und natürlich können Sie nur einen 64-Bit-JVM ausgeführt werden, wenn das Host-Betriebssystem 64-Bit-fähig ist, und Sie sind mit 64-Bit-fähiger Hardware. (Wenn Sie einen Pentium haben, Sie sind einfach kein Glück.)

  5. Schließlich kommt die Menge des physischen Speichers in Ihrem System ins Spiel. In der Theorie können Sie Ihre JVM bitten, einen Haufen zu verwenden, etc., die oft größer als als Ihre Maschine physischer Speicher ist. In der Praxis ist dies eine schlechte Idee . Wenn Sie über den virtuellen Speicher zuweisen, wird Ihr System dreschen und Anwendungsleistung wird den Boden gehen.

Die wegzunehmen, ist dies:

  • Wenn Sie eine 32-Bit-JVM verwenden, werden Sie wahrscheinlich irgendwo zwischen 2**31 und 2**32 begrenzt Bytes adressierbarer Speicher. Das ist genug Platz für maximal zwischen 2**29 und 2**30 verdoppelt, ob Sie ein Array oder ein zugeordnetes Buffer verwendet werden.

  • Wenn Sie einen 64-Bit-JVM verwenden, können Sie ein einzelnes Array von 2**31 verdoppelt darstellen. Die theoretische Grenze eines zugeordneten Puffers würde 2**63 Bytes oder 2**61 verdoppelt, aber die praktische Grenze würde in etwa die Menge an physikalischem Speicher des Gerätes hat.

  • sein

Andere Tipps

Wenn der Speicher eine Dateizuordnung ist es möglich, von Adressraum laufen in 32-Bit-VM. Dies geschieht auch, wenn die Datei in kleine Stücke abgebildet wird und diese ByteBuffers sind nicht mehr erreichbar. Der Grund dafür ist, dass GC nie Tritte in die Puffer freizugeben.

Siehe den Bug unter http://bugs.sun.com/bugdatabase /view_bug.do?bug_id=6417205

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top