2008-12-01 2 views
7

Недавно я наткнулся на это в какой-то код - в основном кто-то пытается создать большой объект, справляясь, когда там не хватает кучи, чтобы создать его:Java: достаточно свободной кучи для создания объекта?

try { 
    // try to perform an operation using a huge in-memory array 
    byte[] massiveArray = new byte[BIG_NUMBER]; 
} 
catch (OutOfMemoryError oome) { 
    // perform the operation in some slower but less 
    // memory intensive way... 
} 

Это не кажется правильным, так как Sun сами рекомендуем вам не следует пытаться поймать Error или его подклассы. Мы обсуждали это, и еще одна идея, которая пришла явным образом проверить для свободного кучи:

if (Runtime.getRuntime().freeMemory() > SOME_MEMORY) { 
    // quick memory-intensive approach 
} 
else { 
    // slower, less demanding approach 
} 

Опять же, это кажется неудовлетворительным - особенно в том, что выбирая значение для SOME_MEMORY трудно легко относиться к работе в вопрос: для какой-нибудь произвольный большой объект, как я могу оценить, сколько памяти может потребоваться для его создания?

Есть ли лучший способ сделать это? Возможно ли это на Java или есть идея управлять памятью ниже уровня абстракции самого языка?

Edit 1: в первом примере, это может быть на самом деле представляется возможным оценить количество памяти byte[] заданной длины может занимать, но есть более универсальный способ, который распространяется и на сколь угодно больших объектов?

Edit 2:, как @erickson указывает, есть способы, чтобы оценить размер объекта после его создания, но (не обращая внимания на статистический подход, основанный на предыдущих размеров объекта) есть способ сделать это для еще -установленные объекты?

Есть также некоторые споры относительно того, разумно ли поймать OutOfMemoryError - кто-нибудь знает что-нибудь убедительное?

ответ

5

freeMemory не совсем прав. Вам также нужно добавить maxMemory() - totalMemory(). например предполагая, что вы запускаете виртуальную машину с max-memory = 100M, JVM может во время вызова вашего метода использовать только (из ОС) 50M. Из этого, скажем, 30M фактически используется JVM. Это означает, что вы покажете 20M бесплатно (примерно, потому что мы говорим только о куче здесь), но если вы попытаетесь сделать свой более крупный объект, он попытается захватить другие 50M, которые его контракт позволяет ему взять с ОС перед отказом и ошибкой. Таким образом, вы фактически (теоретически) получили бы 70M.

Чтобы сделать это более сложным, 30M, который он сообщает, как используется в приведенном выше примере, включает материалы, которые могут иметь право на сбор мусора. Таким образом, на самом деле у вас может быть больше доступной памяти, если он достигнет потолка, он попытается запустить GC, чтобы освободить больше памяти.

Вы можете попытаться обойти этот бит вручную вызвав System.gc, за исключением того, что это не такая уж очень хорошая вещь, чтобы сделать, потому что

-это не гарантированно работать сразу

-это воля остановите все на своих дорожках, пока он работает

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

2

Я не считаю, что существует разумный, общий подход к этому, который можно смело полагать на 100% надежным. Даже подход Runtime.freeMemory уязвим к тому, что на самом деле у вас может быть достаточно памяти после сбора мусора, но вы не знали бы этого, если только вы не нажмете gc. Но тогда нет надежного способа заставить GC тоже. :)

Сказав это, я подозреваю, что вы действительно знали приблизительно, сколько вам нужно, и запустили System.gc() заранее, и ваш запуск в простом однопоточном приложении, у вас будет достаточно приличный выстрел, чтобы получить его правильно с вызовом .freeMemory.

Если какое-либо из этих ограничений терпит неудачу, и вы получаете ошибку OOM, ваша спина на квадратной, и поэтому, вероятно, не лучше, чем просто перехватывать подкласс Error. Несмотря на некоторые риски, связанные с этим (VM от Sun не дает много гарантий относительно того, что происходит после OOM ... существует некоторый риск внутреннего коррупции), есть много приложений, для которых просто ловить его и продолжать жизнь не оставит вас без серьезного ущерба.

Более интересный вопрос, на мой взгляд, однако, почему существуют случаи, когда у вас достаточно памяти для этого и других, где вы этого не делаете? Может быть, еще один анализ связанных с производительностью компромиссов - это реальный ответ?

+0

Вы столкнулись с проблемой реальной жизни на голове - фактическое исправление заключалось в том, чтобы переписать метод так, чтобы ему не потребовалось много памяти для начала. Вопрос был более академичным, но все же интересным ... – 2008-12-01 20:09:30

+0

@jsight на тему «Почему существуют случаи, когда ..» Как насчет вас (и я не говорю, что это должно быть сделано в памяти), зашифровавшего файл файловой системы. Когда-то весь файл равен 1 мб, другие - 12 ГБ. ? Когда это произойдет, нет возможности выпускать достаточное количество ОЗУ! – OscarRyz 2008-12-01 21:05:08

+0

@ Оскар: Я не думаю, что попытаюсь выяснить свободную память, чтобы определить, как бороться с этими двумя случаями.Я с большей вероятностью буду основывать свое решение на размере файла с размером блока, определяемым на основе памяти sys при запуске, а не телеметрией в реальном времени. – jsight 2008-12-01 22:58:59

2

Определенная ошибка - наихудший подход. Ошибка возникает, когда нет НИЧЕГО, вы можете сделать это. Даже не создавайте журнал, слоеный, как «... Хьюстон, мы потеряли виртуальную машину».

Я не совсем понял вторую причину. Это было плохо, потому что трудно связать SOME_MEMORY с операциями? Не могли бы вы перефразировать это для меня?

Единственной альтернативой я вижу, чтобы использовать жесткий диск в качестве памяти (RAM/ROM, как и в старые времена), я думаю, что это то, что вы указываете в своем «еще более медленным, менее требовательный подход»

Каждая платформа имеет свои пределы, Java Suppport столько, сколько оперативной памяти ваше оборудование готовы дать (а на самом деле вы по настройке VM) в вС JVM осущ, что может быть сделано с

-Xmx 

Вариант

как

java -Xmx8g some.name.YourMemConsumingApp 

Например

Конечно, вы можете в конечном итоге пытается выполнить операцию, которая занимает 10 ГБ оперативной памяти

Если это ваш случай, то вам обязательно надо поменять на диск.

Кроме того, использование шаблона стратегии может сделать более приятный код. Хотя здесь это выглядит излишеством:

if(isEnoughMemory(SOME_MEMORY)){ 
    strategy = new InMemoryStrategy(); 
}else{ 
    strategy = new DiskStrategy(); 
} 

strategy.performTheAction(); 

Но это может помочь, если «еще» включает в себя много кода и выглядит плохо. Кроме того, если каким-то образом вы можете использовать третий подход (например, используя облако для обработки), вы можете добавить третью стратегию

... 
strategy = new ImaginaryCloudComputingStrategy(); 
... 

: P

EDIT

После того, как проблемы со вторым подход: Если есть несколько раз, когда вы не знаете, сколько ОЗУ будет потреблено, но вы знаете, сколько у вас осталось, вы можете использовать смешанный подход (ОЗУ, когда у вас достаточно, ROM [диск], когда вы не)

Предположим, что это теоретическая проблема.

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

Затем вы выполняете некоторую операцию над этим потоком (зашифруйте его, например).

Если вы используете только ОЗУ, это будет очень быстро, но если файл достаточно велик, чтобы потреблять всю вашу память APP, вам необходимо выполнить некоторую операцию в памяти, а затем выполнить обмен файлами и сохранить временные данные там.

VM будет GC, когда закончится нехватка памяти, вы получите больше памяти, а затем выполните другой фрагмент. И это повторяется до тех пор, пока вы не обработаете большой поток.

while(!isDone()) { 
     if(isMemoryLow()){ //Runtime.getRuntime().freeMemory() < SOME_MEMORY + some other validations 
      swapToDisk(); // and make sure resources are GC'able 
     } 
     byte [] array new byte[PREDEFINED_BUFFER_SIZE]; 
     process(array); 

     process(array); 
} 
cleanUp(); 
2

Есть kludges that you can use to estimate the size of an existing object; вы можете адаптировать некоторые из них, чтобы предсказать размер еще не созданного объекта.

Однако в этом случае, я думаю, лучше всего поймать ошибку. Прежде всего, при запросе свободной памяти не учитывается то, что доступно после сбора мусора, которое будет выполнено до повышения OOME. И, запрашивая сбор мусора с System.gc(), является ненадежным. Он часто явно отключен, потому что он может повредить производительность, и если он не отключен & hellip; ну, это может повредить производительность при ненужном использовании.

Невозможно восстановить большинство ошибок. Однако возможность восстановления зависит от вызывающего, а не от вызываемого абонента. В этом случае, если у вас есть стратегия восстановления с OutOfMemoryError, это действительно важно, чтобы поймать его и отступить.

Я думаю, что на практике это действительно сводится к разнице между «медленным» и «быстрым» способом. Если «медленный» метод будет достаточно быстрым, я буду придерживаться этого, так как он безопаснее и проще. И, мне кажется, что он позволяет использовать его в качестве отскока, означает, что он «достаточно быстро». Не позволяйте небольшим оптимизациям свести на нет надежность вашего приложения.

2

Подход «попробуйте распределить и обработать ошибку» очень опасен.

  • Что делать, если вы едва получаете свою память? Возможно, произойдет более позднее исключение OOM, потому что вы слишком близко подошли к ограничениям. Почти любой вызов библиотеки будет выделять память как минимум на короткое время.
  • Во время выделения другой поток может получить исключение OOM при попытке выделить относительно небольшой объект. Даже если ваше распределение обречено на провал.

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

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