2011-12-18 3 views
4

Рассмотрите приложение, которое создает 5-6 потоков, каждый поток в цикле выделяет MappedByteBuffer для размера страницы 5mb.Предотвращение OutOfMemory при использовании java.nio.MappedByteBuffer

MappedByteBuffer b = ch.map(FileChannel.MapMode.READ_ONLY, r, 1024*1024*5); 

Рано или поздно, когда приложение работает с большими файлами, оот брошено

java.io.IOException: Map failed at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:758) 
Caused by: java.lang.OutOfMemoryError: Map failed 
     at sun.nio.ch.FileChannelImpl.map0(Native Method) 
     at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:755) 

Согласно спецификации, MappedBuffer следует располагать прямой памяти, как только он сам GC. Похоже, проблема заключается в том, что MappedBuffer's GC-ed слишком поздно, а затем завершена прямая память.

Как избежать этой ситуации? Вероятно, MappedBuffer может использовать неявно или использовать какой-то пул MappedBuffer

+0

любопытства, что ваш код делает? – fge

+1

Исключение составляет ** NOT OOM **, но IOException. У вас закончилось виртуальное адресное пространство. Покажите еще немного кода. Mapped buffers и reclamation в java - это долговременные проблемы (все еще нерешенные изящно) – bestsss

ответ

2

Возможно, WeakHashMap для объединения этих MappedBuffers будет работать.

Но прежде чем вы узнаете о первопричине, я бы рекомендовал подключить ваше приложение до Visual VM 1.3.3, со всеми установленными плагинами, чтобы вы могли точно видеть, что вызывает ошибку OOM. Вы предполагаете, что эти MappedBuffers делают это, но они всего 5 МБ каждый для 5-6 потоков - всего 25-30 МБ.

Лучше иметь данные, чем предполагать. Визуальная виртуальная машина получит его для вас.

+0

На самом деле, каждый поток может создавать много буфера в цикле, зависит от размера файла. Это означает, что общее количество буферов не ограничено. Вероятно, я должен использовать какой-то семафор, чтобы предотвратить выделение огромного объема памяти. В любом случае, я попробую профайлер, вы предложили – user12384512

+0

Mapped буферы не могут быть объединены! Они создаются для покрытия адреса, возвращаемого виртуальным адресом сопоставления, в местоположение в файле и загрузки данных по ошибкам страницы + запись на дескриптор файла закрыть или запросить. – bestsss

+1

... и 'WeakHashMap' абсолютно бесполезны/вредны, поскольку ключ не будет самим буфером, поэтому он не будет доступен только GC, прежде чем« WeakHashMap »будет удален (изнутри). Использование зависимой от GC структуры для проблемы, безусловно, неверно. – bestsss

3

В сообщении об ошибке говорится, что «карта не удалась», а не «пустое место» или «пространство смены». Это означает, что JVM не хватает адресного пространства.

См. this bug в базе данных Sun, а также this question.

Первое звено обеспечивает обходной путь (Ewww), который является близко, что второе звено говорит:

try { 
     buffer = channel.map(READ_ONLY, ofs, n); 
    } catch (java.io.IOException e) { 
     System.gc(); 
     System.runFinalization(); 
     buffer = channel.map(READ_ONLY, ofs, n); 
    } 
+0

В зависимости от алгоритмов GC и его параметров вызов 'System.gc()' может быть NOP. Использование '-XX: + DisableExplicitGC' эффективно удаляет уродливый патч catch OOM и повторяет попытку после запроса GC – bestsss

4

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

public static void clean(ByteBuffer bb) { 
    if(bb == null) return; 
    Cleaner cleaner = ((DirectBuffer) bb).cleaner(); 
    if(cleaner != null) cleaner.clean(); 
} 

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

Может быть, вы можете посмотреть на создание более крупных ByteBuffers реже (если у вас есть большое количество файлов) Создание MappedByteBuffer не бесплатно (занимает около 50 микросекунд на некоторых машинах)

+0

К сожалению, эти методы, похоже, что-то делают, но на самом деле они не деактивируют ByteBuffer как таковой. Этот метод работает только для прямой памяти. –

0

MappedBuffer следует утилизировать прямая память, как только она сама GC

На самом деле это не говорит, что где-нибудь, что я вижу. Есть давний пункт Парада ошибок, который гласит, что это никогда выпущено.

Он сказал так:

Поэтому рекомендуется, чтобы прямые буферы будут выделены в первую очередь для крупных долгоживущих буферов

+1

Они уверены, что основная проблема заключается в том, что крошечный Java-объект имеет ссылку на мегабайты, и GC не считает его безотлагательным для выпуска и завершения/деактивации виртуальной памяти. – bestsss

+0

@bestsss Либо они «расположены», либо нет. Прими решение. Вам нужно посмотреть Bug Parade item #4724038. Это не согласуется с вами. – EJP

+1

снова прочитайте комментарий. они расположены, но слишком часто задерживаются. Я хорошо знаю ошибку и свой собственный язык как для Windows, так и для Linux/Solaris. Ошибка не говорит о том, что сопоставленные буферы не размонтированы, они ПОСЛЕ того, как ByteBuffer (и его представления) собирают мусор. Они просто не могут быть принудительно закрыты/не связаны обычными средствами. Всегда можно было взломать и разблокировать буфер, рискующий SIGSEV или используя другой файл, или любой неопределенный результат. – bestsss

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