2015-04-20 5 views
2

У меня возникает очень странная проблема, которую я хотел бы воспроизвести в SSCCE, но я не могу.OutOfMemoryError перед достижением максимума кучи пространства?

Я запускаю свою программу в Java8 (32 бит) с помощью -Xmx1024m, этот код загружает довольно большой файл (120 МБ) в массив байтов с использованием FileInputStream.

Проблема в том, что, хотя в Java6 у меня не было проблем, в Java8 я получаю исключения OutOfMemoryError, как только я пытаюсь загрузить его.

У меня все еще много свободного места, и я профилировал его, и я не вижу проблем.

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

Я знаю, что Oracle избавился от PermGen, но как это может повлиять на мою программу?

Я также прочитал, что это может быть связано с проблемой фрагментации кучного пространства, но я попытался отладить его, профилировать и запустить цикл GC из профайлера непосредственно перед распределением памяти, и он все еще остается (я предполагаю, что цикл GC будет дефрагментировать пустое пространство)

+0

первый, проверить сообщение о OOME, он не должен быть куча ограниченность пространства. во-вторых, используйте профилировщик, посмотрите, что потребляет (больше) память. – the8472

+0

он не содержит никакого сообщения «null» – lqbweb

+0

Выполняется то же самое, с тем же -Xmx1024m, но в 64 бит работает. – lqbweb

ответ

0

Я думаю, что нашел причину. Согласно этому сообщению об ошибке:

https://bugs.openjdk.java.net/browse/JDK-6478546

FileInputStream оставляет пространство непосредственно в родную память. Это на самом деле хуже при увеличении размера пространства Макс. Кучи, поскольку на самом деле меньше мест для собственных карт.

Причина, по которой мой код работает с 64-разрядным кодом Java8, заключается именно в том, что у меня больше виртуального пространства от Windows, а не в ~ 1.7Gb в Windows.

Мне все еще не очень понятно, почему тот же код работает в Java6. Кажется, что одно и то же приложение использует больше родного пространства в Java8, чем в Java6. Возможно, новая схема с Metaspace немного изменила ситуацию. Я не уверен.

Интересная статья о нем:

http://www.codingthearchitecture.com/2008/01/14/jvm_lies_the_outofmemory_myth.html

4

С Java8 в кучу, которые были частью PermGenSpace, были некоторые вещи, которые раньше были, например, пулом строк, также проверяли размер старого поколения, который он выделяет

+0

В VisualGC пул старого поколения по-прежнему имеет 600 МБ бесплатно. – lqbweb

+0

, то вы, вероятно, пытаетесь создать объект, который создается сначала в молодом поколении, а молодой ген слишком мал для него. –

0

Пара вещей.

  • 1 GB не много, даже на Windows 32 бит вы можете сделать это 1400 МБ.
  • Java 8 работает по-разному с Java 6 и оптимизируется с предположением, что память дешевле, чем когда Java 6 была выпущена 9 лет назад.
  • вы можете карта памяти в 120 МБ и вообще не использовать кучу. Однако это может быть более эффективным подходом.
  • Потребляемая память почти наверняка будет делать то, что вы делаете с данными, которые могут быть во много раз больше размера файла. Я бы не предположил, что на самом деле вы используете только 120 МБ для обработки этого файла.

Я предлагаю вам профиль памяти приложения с большим количеством памяти, используя 64-битную JVM, если вам нужно понять, где потребляется память.

+0

спасибо. Я уже сделал то, что вы предлагаете для профилирования приложения с 2 ГБ в 64 битах, при выполнении операции, которая потребовалась, например, 270 Мб от Eden Space, в 32 битах максимальное пространство Eden - это 250 Мб, но весь накопительный пул по-прежнему имеет примерно 600 МБ свободно. Я понимаю ваше другое предложение о переносе этого выделения в собственную память, но этот код вышел из-под контроля (это библиотека). Все, что я хочу, это понять причину этого. Еще раз спасибо! – lqbweb

+0

@Ruben вы можете вызвать jvm, чтобы взять кучу кучи в точке, где вы получаете OOME, это должно сделать более понятным относительно того, что использует пространство в этой точке –

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