Хотя отображаемый буфер может использовать меньшую физическую память в любой момент времени, для него все еще требуется доступное (логическое) адресное пространство, равное суммарному (логическому) размеру буфера. Чтобы ухудшить ситуацию, он может (возможно) потребовать, чтобы адресное пространство было смежным. По какой-то причине этот старый компьютер не может обеспечить достаточное дополнительное логическое адресное пространство. Двумя вероятными объяснениями являются (1) ограниченное логическое адресное пространство + весовые требования к буферной памяти и (2) некоторое внутреннее ограничение, которое ОС накладывает на объем памяти, который может быть отображен как файл для ввода-вывода.
Что касается первой возможности, рассмотрим тот факт, что в системе виртуальной памяти каждый процесс выполняется в своем собственном логическом адресном пространстве (и, следовательно, имеет доступ к полной адресации по адресу 2^32 байта). Поэтому, если - в момент времени, когда вы пытаетесь создать экземпляр MappedByteBuffer
- текущий размер процесса JVM плюс общий (логический) размер MappedByteBuffer
превышает 2^32 байта (~ 4 гигабайта), тогда вы столкнулись бы с OutOfMemoryError
(или любой другой ошибкой/исключением, выбранным классом, например, IOException: Map failed
).
Что касается второй возможности, возможно, самый простой способ оценить это - профилировать вашу программу/JVM при попытке создать экземпляр MappedByteBuffer
. Если выделенная память JVM-процесса + требуемая totalTargetSize
значительно ниже потолка 2^32 байта, но вы по-прежнему получаете ошибку с ошибкой «карта», то вполне вероятно, что некоторые внутренние ограничения ОС на размер файлов с отображением памяти первопричиной.
Итак, что это значит, насколько это возможно, решения?
- Просто не используйте этот старый компьютер.(желательно, но, вероятно, не представляется возможным)
- Удостоверьтесь, что все остальное в вашей JVM имеет как можно меньшую площадь памяти для срока службы
MappedByteBuffer
. (правдоподобно, но, возможно, нерелевантно и определенно непрактично)
- Разбить этот файл на более мелкие куски, а затем работать только на одном куске за раз. (может зависеть от характера файла)
- Используйте другой буфер меньшего размера. ... и просто терпит снижение производительности. (это наиболее реалистичным решением, даже если это самый расстраивает)
Кроме того, что именно является totalTargetSize
для проблемной случае?
EDIT:
После выполнения некоторого копания, представляется очевидным, что IOException обусловлено running out of address space in a 32-bit environment. Это может произойти даже тогда, когда сам файл находится под 2^32 байтами из-за отсутствия достаточного смежного адресного пространства или из-за других достаточно больших требований адресного пространства в JVM в то же время в сочетании с большими MappedByteBuffer
запрос (see comments). Чтобы быть ясным, IOE все еще может быть выброшен, а не OOM even if the original cause is ENOMEM. Более того, по-видимому, существуют проблемы со старыми [вставить здесь Microsoft OS] 32-разрядными средами, в частности (example, example).
Так что, похоже, у вас есть три основных варианта.
- Использовать «the 64-bit JRE or...another operating system» в целом.
- Используйте меньший буфер другого типа и работайте с файлом в кусках. (и принять удар производительности из-за того, что не используется сопоставленный буфер)
- Продолжите использовать
MappedFileBuffer
по соображениям производительности, но также можете работать с файлом в меньших кусках, чтобы обойти ограничения адресного пространства.
Причины я положить используя MappedFileBuffer
в более мелких кусках, как третий из-за устоявшиеся и нерешенные проблемы в unmapping MappedFileBuffer
(example), которая является то, что вы обязательно должны сделать между обработкой каждого фрагмента в чтобы избежать попадания 32-битного потолка из-за суммарного адресного пространства накопленных сопоставлений. (ПРИМЕЧАНИЕ: это применимо только в том случае, если это 32-битный потолок адресного пространства, а не некоторые внутренние ограничения ОС, которые являются проблемой ... если последнее, а затем игнорировать этот абзац) Вы можете попробовать this strategy (удалите все ссылки, затем запустите GC), но вы, по сути, будете во власти того, как GC и ваша базовая ОС взаимодействуют в отношении файлов с отображением памяти. И другие потенциальные обходные пути, которые пытаются манипулировать базовым файлом с отображением памяти более или менее напрямую (example), чрезвычайно опасны и специально осуждены Oracle (see last paragraph). Наконец, учитывая, что поведение GC по-прежнему ненадежно, и, кроме того, официальная документация явно заявляет, что «many of the details of memory-mapped files [are] unspecified», я бы не рекомендую использовать MappedFileBuffer
, как это независимо от любого обходного пути, о котором вы можете прочитать.
Так что, если вы не хотите рисковать, я предлагаю либо следовать явным советам Oracle (пункт 1), либо обрабатывать файл как последовательность меньших фрагментов с использованием другого типа буфера (точка 2).
Он потерпел неудачу, потому что он просто не может выделить столько места _address_ на 32 бит. Речь идет не о физической нехватке оперативной памяти. –
, но размер файла составляет всего 200 Мб, это должно быть хорошо shoudnt it –
Вы не можете быть уверены, ему нужен смежный блок такого размера. –