Почему я получаю сообщение “Недостаточно места для обработки этой команды”, используя Java MappedByteBuffers?

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

  •  20-09-2019
  •  | 
  •  

Вопрос

У меня есть очень большой массив двойников, для обработки которых я использую файл на диске и список подкачки MappedByteBuffers, см. этот вопрос для получения дополнительной информации.Я работаю на Windows XP, используя Java 1.5.

Вот ключевая часть моего кода, которая выполняет распределение буферов по файлу...

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

Я получаю ошибку, упомянутую в названии, после того, как выделяю второй или третий буфер.

Я думал, что это как-то связано с доступной непрерывной памятью, поэтому попробовал использовать ее с разными размерами и количеством страниц, но безрезультатно.

Что именно делает "Недостаточно места для обработки этой команды" имею в виду и что, если вообще что-нибудь, я могу с этим поделать?

Я думал, что смысл MappedByteBuffers заключается в возможности обрабатывать структуры большего размера, чем вы могли бы уместить в куче, и обрабатывать их так, как если бы они находились в памяти.

Есть какие-нибудь зацепки?

Редактировать:

В ответ на приведенный ниже ответ (@adsk) я изменил свой код, чтобы у меня никогда не было более одного активного MappedByteBuffer одновременно.Когда я ссылаюсь на область файла, которая в данный момент не сопоставлена, я удаляю существующую карту и создаю новую.Я все еще получаю ту же ошибку примерно после 3 операций с картой.

Ошибка, указанная в GC, не собирающая MappedByteBuffers, по-прежнему кажется проблемой в JDK 1.5.

Это было полезно?

Решение

Я думал, что смысл MappedByteBuffers заключается в возможности обрабатывать структуры большего размера, чем вы могли бы уместить в куче, и обрабатывать их так, как если бы они находились в памяти.

Нет.Идея состоит в том , чтобы позволить вам обращаться более чем к 2 ** 31 двойнику ...исходя из предположения, что у вас было достаточно памяти и вы использовали 64-разрядную JVM.

(Я предполагаю, что это последующий вопрос к этот вопрос.)

Редактировать:Очевидно, что требуется больше объяснений.

Существует ряд ограничений, которые вступают в игру.

  1. Java имеет фундаментальное ограничение, заключающееся в том, что length атрибут массива, а индексы массива имеют тип int.Это, в сочетании с тем фактом, что int имеет знак, и массив не может иметь отрицательный размер, означает, что максимально возможный массив может иметь 2**31 элементы.Это ограничение распространяется на 32-битные И 64-битные виртуальныемашины.Это фундаментальная часть языка Java ...нравится тот факт, что char значения идут от 0 Для 65535.

  2. Использование 32-битной JVM устанавливает (теоретическую) верхнюю границу 2**32 от количества байтов, которые могут быть адресованы JVM.Это включает в себя всю кучу, ваш код и библиотечные классы, которые вы используете, ядро собственного кода JVM, память, используемую для отображенных буферов...все.(На самом деле, в зависимости от вашей платформы, операционная система мочь дать вам значительно меньше, чем 2**32 байт, если адресное пространство.)

  3. Параметры, которые вы указываете в командной строке Java, определяют, сколько кучи памяти будет занимать JVM разрешить ваше приложение для использования.Память, сопоставленная с использованием MappedByteBuffer объекты не учитываются при этом.

  4. Объем памяти, который предоставит вам ОС, зависит (в Linux / UNIX) от общего объема настроенного пространства подкачки, ограничений "процесса" и так далее.Аналогичные ограничения, вероятно, применимы и к Windows.И, конечно, вы можете запустить 64-разрядную JVM, только если ОС хоста поддерживает 64-разрядную версию, и вы используете 64-разрядное оборудование.(Если у вас Pentium, вам явно не повезло.)

  5. Наконец, в игру вступает объем физической памяти в вашей системе.Теоретически, вы можете попросить свою JVM использовать кучу и т.д., Которая во много раз больше физической памяти вашего компьютера.На практике это представляет собой плохая идея.Если вы чрезмерно выделяете виртуальную память, ваша система будет работать с перебоями, а производительность приложений упадет до минимума.

Еда на вынос - это:

  • Если вы используете 32-разрядную JVM, вы, вероятно, ограничены где-то между 2**31 и 2**32 байты адресуемой памяти.Этого пространства достаточно максимум для 2**29 и 2**30 удваивается независимо от того, используете ли вы массив или отображенный буфер.

  • Если вы используете 64-разрядную JVM, вы можете представить один массив 2**31 удваивается.Теоретический предел отображенного буфера был бы равен 2**63 байты или 2**61 удваивается, но практический предел примерно равен объему физической памяти, имеющейся на вашем компьютере.

Другие советы

При сопоставлении файла с памятью в 32-разрядной виртуальной машине может закончиться адресное пространство.Это происходит, даже если файл сопоставлен небольшими фрагментами и эти байтебуферы больше недоступны.Причина в том, что GC никогда не запускается для освобождения буферов.

Обратитесь к ошибке по адресу http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top