2009-12-18 3 views
2

У меня есть очень большой массив парных разрядов, в котором я использую диск-файл и пейджинговый список MappedByteBuffers для обработки, см. this question для получения дополнительной информации. Я работаю в Windows XP с использованием Java 1.5.Почему я получаю «Недостаточно памяти для обработки этой команды» с использованием Java MappedByteBuffers?

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

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 была способностью обрабатывать структуры, большие, чем вы могли бы вместить в кучу, и относиться к ним так, как если бы они были в памяти.

Любые подсказки?

EDIT:

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

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

+0

Перед тем, как сделать вывод о том, что GC ошибка все еще существует, вам нужно запустить свой код через профайлер использования памяти, чтобы убедиться, что его не висит на ссылки ByteBuffer. –

ответ

3

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

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

(я предполагаю, что это Followup вопрос this question.)

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

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

  1. Java имеет фундаментальное ограничение, что атрибут length массива, а индексы массива имеют тип int. Это, в сочетании с тем фактом, что int подписан, и массив не может иметь отрицательный размер, означает, что наибольший возможный массив может содержать 2**31 элементов. Это ограничение применяется к 32-битным и 64-битным JVM. Это фундаментальная часть языка 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 байт адресуемой памяти. Этого достаточно для MAXIMUM между 2**29 и 2**30 удваивает, используете ли вы массив или сопоставленный буфер.

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

+0

Если бы у меня была 64-разрядная JVM, мне все равно нужна помощь для решения более чем 2^31 удвоений? – Simon

+0

Да. Проблема в том, что при использовании 32-разрядной JVM все должно вписываться в эти 2 ** 32 байта. Если он не подходит, вы не можете использовать его как структуру данных в памяти. –

+0

Я, очевидно, немного тускнею. У меня большой файл (намного больше, чем 2^32), и я должен был получить его куски в 32-битной среде (JVM/OS/etc.). Мои MappedByteBuffers никогда не превышают (Integer.MAX_VALUE/256), что означает, что я никогда не обращаюсь напрямую к чему-то большему, чем может обрабатывать int. Первая пара mappngs работает нормально, последующие не работают. Что я не понимаю? Как предполагается использовать MappedByteBuffer?Если он никогда не сможет адресовать нечто большее, чем то, что может быть вписано в память, в чем его смысл? – Simon

1

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

Направить ошибка при http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205

+0

см. Править выше ... – Simon

+0

Обратите внимание, что Sun >> думаю <<, что ошибка исправлена, и есть только один комментарий о том, что это не так. Этот комментарий может быть просто ошибкой в ​​приложении комментатора, который висит на ссылках на ByteBuffers, в результате чего они не имеют права на сбор мусора. –

+0

Обратите также внимание, что ошибка была исправлена ​​ДЛИННЫМ временем назад. Если вы все еще используете уязвимую JVM ... вы не должны быть! –

Смежные вопросы