Как определить, когда куча Java почти заполнена, и предотвратить "OutOfMemoryError: Java Heap"?

В SO довольно много вопросов, связанных с ошибкой "OutOfMemoryError: Java Heap", но читая их, большинство, похоже, обсуждают, как увеличить размер кучи или профилировать приложение и обнаружить утечки памяти.

Я работаю над проектом, который включает анализ стоимости ветки и связанного алгоритма. Для входных данных небольшого размера потенциальное число решений для поиска увеличивается при O(n!). При определенном размере ввода n я столкнулся с "OutOfMemoryError", потому что частичные решения хранятся в очереди с приоритетами до тех пор, пока они не будут готовы к обработке, а огромное количество частичных решений в очереди заполняет память. Итак, я знаю, что у меня нет утечки памяти, и я не обязательно хочу увеличивать размер кучи.

То, что я хотел бы сделать, это просто определить, когда память почти заполнена, а затем дать пользователю сообщение, в котором сообщается, что происходит и почему программа завершается (необязательно, чтобы программа продолжала функционировать в этот момент). Есть ли способ сделать это? Я посмотрел на java.lang.management пакет, но это не имеет особого смысла для меня, и мне было трудно найти достойный пример кода. Любое объяснение или пример кода приветствуется.

5 ответов

Решение

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

Это сложно. Главным образом потому, что свободная память на самом деле не известна до того, как происходит сборка мусора, а серьезная сборка мусора обычно происходит только перед тем, как закончится.

Что вы можете сделать, это объяснить, почему программа потерпела крах после факта: Eclipse делает это, например. Вы можете перехватить OutOfMemoryError, как и любой другой Throwable, и показать свое сообщение:

 try{
      heavyLifting();
 }
 catch (OutOfMemoryError e){
    showAMessage();   
 }

Самое простое и прямое решение - поймать OutOfMemoryError на очень высоком уровне приложения, а затем просто показать сообщение, а затем закройте приложение.

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

Вы можете получить статус памяти, используя Runtime апи;

        // Memory status
        Runtime runtime = Runtime.getRuntime();

        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();

        boolean memoryOK = minFree <= freeMemory;

Возможно, вы захотите взглянуть на это: http://docs.oracle.com/javase/6/docs/technotes/guides/management/mxbeans.html и, более конкретно, раздел, связанный с "порогом" памяти.

Я не думаю, что вы можете сделать большую часть от повышения эффективности памяти вашего кода. Даже если вы узнаете, что память почти заполнена, и теперь out of memory error скоро придет, вы можете сделать очень мало, в лучшем случае вы можете запросить сборщик мусора, чтобы он стал активным, но это запрос. И будьте уверены, JVM сделает все возможное, чтобы предотвратить ошибку нехватки памяти. Если она все еще появляется, то, скорее всего, это было неизбежно с данным кодом.

Другие вопросы по тегам