2009-11-07 2 views
47

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

ответ

78

Это не исключение; это ошибка: java.lang.OutOfMemoryError

Вы может поймать его, как это происходит от Throwable:

try { 
    // create lots of objects here and stash them somewhere 
} catch (OutOfMemoryError E) { 
    // release some (all) of the above objects 
} 

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

+14

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

+0

@matt b - в конкретном случае, когда вы ** можете ** поймать его, вы, по-видимому, пытаетесь контролировать потребление памяти и, следовательно, могли бы выпустить некоторые из них. Но, вообще говоря, вы правы, конечно. – ChssPly76

+0

@ ChssPly76 (и другие) - пожалуйста, прочитайте мой ответ, чтобы понять, почему попытка управлять потреблением памяти, поймав OOME, будет ** BAD IDEA **. –

1

Конечно, ловить OutOfMemoryError разрешено. Убедитесь, что у вас есть план, что делать, когда это произойдет. Вам необходимо освободить некоторую память (отбрасывая ссылки на объекты), прежде чем выделять еще какие-либо объекты, или у вас снова закончится нехватка памяти. Иногда простой акт разматывания стека несколькими кадрами сделает это за вас, иногда вам нужно делать что-то более явное.

+1

Ваш план должен включать стратегию для устранения возможности того, что ваша программа находится в противоречивом состоянии; см. http://stackoverflow.com/questions/8728866/no-throw-virtualmachineerror-guarantees – Raedwald

48

Возможно:

try { 
    // tragic logic created OOME, but we can blame it on lack of memory 
} catch(OutOfMemoryError e) { 
    // but what the hell will you do here :) 
} finally { 
    // get ready to be fired by your boss 
} 
+10

Там * есть * по крайней мере одна разумная вещь, которую вы могли бы сделать, которая вызывает OOME и восстанавливается: загрузка очень большого изображения. Единственное, что в блоке try является вызовом ImageIO.read(), и вы показываете пользователю диалоговое окно, сообщающее им, что изображение слишком велико в блоке catch. Просто сказать ... – uckelman

+1

@uckelman Это неправильный подход, если ваше приложение не имеет нити. Дело в том, что даже если ваш образ-погрузка-поток улавливает OOME, но другие потоки не знают об этом и могут получить OOME слишком –

+4

@SurajChandran: OOME, вызванный попыткой загрузить очень большое изображение, будет вызван попыткой выделяют очень большой 'byte []' или 'int []'. Вы получаете OOME от этого, потому что распределение выходит из строя, а не потому, что вы на самом деле не в памяти - следовательно, это не вызовет проблемы для других потоков. – uckelman

1

можно поймать Любое исключение. Просто напишите

try{ 
    // code which you think might throw exception 
}catch(java.lang.Throwable t){ 
    // you got the exception. Now what?? 
} 

В идеале вы не должны поймать java.lang.Error исключения. Невосприимчивость к таким исключениям и разрешение приложения прекратить работу могут оказаться лучшим решением при их возникновении. Если вы думаете, что можете очень хорошо справиться с такой ошибкой, тогда идите вперед.

2

Возможно, но если у вас закончится куча, это не очень полезно. Если есть ресурсы, которые могут быть освобождены от использования SoftReference или WeakReference для таких ресурсов, и их очистка будет автоматической.

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

24

Вы можете поймать и попытаться восстановить из OutOfMemoryError (OOM) исключений, НО ЭТО ВОЗМОЖНО ПЛОХАЯ ИДЕЯ ... особенно если ваша цель состоит в том, чтобы приложение «продолжать идти».

Есть целый ряд причин для этого:

  1. Как уже отмечалось, есть более эффективные способы управления ресурсами памяти, чем явно освободив вещи; то есть использовать SoftReference и WeakReference для объектов, которые могут быть освобождены, если память коротка.

  2. Если вы дождались, когда у вас действительно закончится память, прежде чем освобождать вещи, ваше приложение, скорее всего, потратит больше времени на сборщик мусора. В зависимости от вашей версии JVM и параметров настройки GC, JVM может работать с GC все чаще и чаще, когда он приближается к точке, в которой будет выбрано OOM. Замедление (с точки зрения приложения, выполняющего полезную работу) может быть значительным. Вы, вероятно, хотите этого избежать.

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

Так что мой совет не пытаться продолжать идти от ООМ ... если вы не знаете :

  • где и почему ООМ случилось,
  • , что не будет иметь были любые «побочные убытки» и
  • , что ваше восстановление освободит достаточно памяти для продолжения.
13

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

Чтобы ускорить процесс обхода, мы стараемся хранить как можно больше данных в физической памяти, но структуры данных растут по мере роста двоичного файла, и мы не можем хранить его ВСЕ в памяти (цель состоит в том, чтобы использовать кучу java меньше чем 256 м). так что я делаю?

Я создал резервные копии LinkedLists, Hashtables и т. Д. Это замены для своих копий и реализации всех тех же интерфейсов, которые выглядят одинаково с внешним миром.

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

Есть много причин, по которым у вас может быть нехватка памяти в приложении java. Корень большинства из этих причин является одним из следующих: 1. Приложение запускается на компьютере с ограниченными ресурсами (или пытается ограничить ресурс использование путем ограничения размера кучи) 2. Приложение просто требует больших объемов памяти (было предложено редактирование изображений, но как насчет аудио и видео?) Как насчет компиляторов, как в моем случае? Как насчет долгосрочных сборщиков данных без энергонезависимого хранилища?)

-разрядных

+0

+1 для интересного решения. – sleske

7

можно поймать OutOfMemoryError (это Error, а не Exception), но вы должны знать, что нет способа получить определенное поведение.
Вы даже можете получить еще один OutOfMemoryError, пытаясь поймать его.

Таким образом, лучший способ - создать/использовать память Caves. Существуют некоторые рамки (пример: JCS), но вы можете легко создать свои собственные, используя SoftReference.Существует небольшая статья о том, как ее использовать. here. Следуйте ссылкам в статье, чтобы получить дополнительную информацию.

+2

«нет способа получить определенное поведение»: поскольку «OutOfMemoryError» можно выбросить в любом месте, в том числе в местах, которые могут оставить вашу программу в противоречивом состоянии. См. Http://stackoverflow.com/questions/8728866/no-throw-virtualmachineerror-guarantees – Raedwald

2

Существует, вероятно, по крайней мере, один хорошее время, чтобы поймать OutOfMemoryError, когда вы специально выделить то, что может быть слишком большим:

public static int[] decode(InputStream in, int len) throws IOException { 
    int result[]; 
    try { 
    result = new int[len]; 
    } catch (OutOfMemoryError e) { 
    throw new IOException("Result too long to read into memory: " + len); 
    } catch (NegativeArraySizeException e) { 
    throw new IOException("Cannot read negative length: " + len); 
    } 
    ... 
} 
+0

Действительно ли этот код работает надежно? У меня есть именно этот случай, но я действительно не хочу, чтобы приложение разбилось. Я предполагаю, что альтернатива заключается в том, что (len> someConservativeSize) генерирует новое исключение IOException («слишком долго»); – Quantum7

+3

Этот подход хорошо работает на C++, но я подозреваю, что не так хорошо на Java, потому что связь между 'new' и' OutOfMemoryError' косвенна. Если выделение просто вписывается в память, некоторое время спустя может появиться загадочное 'OutOfMemoryError'. – Raedwald

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