Как улучшить приложение, чтобы избежать проблем с пространством кучи - PullRequest
6 голосов
/ 02 марта 2012

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

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

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

Что-токак "не создавайте объекты в циклах, создавайте их перед циклом и перезаписывайте их внутри" и сортирует.

Ответы [ 5 ]

5 голосов
/ 02 марта 2012

Некоторые баллы:

  • Нет ничего принципиально неправильного в увеличении пространства кучи. Различные приложения, как правило, предъявляют разные требования.
  • Используйте профилировщик, чтобы увидеть, что на самом деле происходит. Например, здесь вы можете найти анализатор кучи: MAT
  • Когда вы узнаете, что экземпляры определенного класса отвечают за 80% потребления кучи:
    • попытаться найти общедоступный набор переменных с одинаковыми значениями. Это кандидаты в один объект, который может использоваться несколькими объектами.
    • Проверьте, храните ли вы некоторую ссылку на относительно большой объектный граф на переменную, которая живет намного дольше, чем ваши циклы (локальные переменные потребляют стек).
    • Пусть ссылки выпадают из области видимости как можно быстрее.
    • Если вы используете внутренние классы, отметьте те, которые не являются статичными, поскольку нестатический внутренний класс содержит ссылку на содержащий объект.
4 голосов
/ 02 марта 2012

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

Профиль, улучшение, повтор

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

РЕДАКТИРОВАТЬ:

Несколько трюков торговли :

  • По возможности делиться объектами вместо дублирования.

  • Остерегайтесь классов коллекции Java (т. Е. Различных реализаций Collection<T> и Map<K,V>).В зависимости от того, что вы храните и какая коллекция используется, вы можете легко увеличить потребление памяти на порядок , не ожидая этого.

  • Хотя Javaне имеет (обычно) утечек памяти в том же смысле, что и в С, в коде Java часто возникают проблемы с объектами, которые остаются живыми после истечения срока их действия.

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

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

2 голосов
/ 02 марта 2012

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

Следующим шагом является выявление общих черт в ваших объектах путем уточнения дизайна или внедрения кэша.Если вы загружаете данные из фиксированного хранилища, то вы можете использовать программные ссылки, так как в JVM заканчивается куча, эти объекты будут GCed (если вы вносите изменения в эти объекты, вам, очевидно, потребуется сохранить их перед удалением жесткой ссылки).Затем, если они понадобятся снова, вашему приложению будет просто необходимо перезагрузить их из резервного хранилища (БД, файлы или что-то еще).

Убедитесь, что вы знаете, как работает GC, и понимаете ссылки на ваши объекты:

  • Сильный / Прямой
  • Мягкий
  • Слабый
  • Phantom

Вот несколько хороших статей, которые объясняют ссылки и GC: * ​​1017 *

http://www.java -tips.org / java-se-tips / java.util /using-weakhashmap-for-listener-lists.html

http://pawlan.com/monica/articles/refobjs/

http://www.kdgregory.com/index.php?page=java.refobj

0 голосов
/ 02 марта 2012

Во-первых, я бы перепроверил свой дизайн, сосредоточив внимание на необходимой верхней сложности ( Ландау / Big O нотация ).

Во-вторых, я бы прочитал Эффективная Java Джоша Блоха, пункт 6 (устранение устаревших ссылок на объекты), чтобы получить некоторые подсказки о

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

В-третьих, если у вас все еще есть исключения OOM, я бы следовал советы Микко .

0 голосов
/ 02 марта 2012

Обнулить любую ссылку на объект, как только объект больше не нужен? Как только объект больше не имеет ссылки на него, сборщик мусора может его забрать, но я предполагаю, что вы уже знали это. GC может также работать с графами объектов без ссылок (если A имеет единственную ссылку на B и больше нет ссылок на A, тогда A и B могут быть собраны).

Вызывать System.gc () практически бессмысленно, поскольку если JVM требуется больше памяти, она будет делать это самостоятельно, а затем использовать освобожденную память. Если он не может освободить больше памяти, вы сталкиваетесь с OOME.

Теперь размер кучи по умолчанию не так велик, и поэтому часто вполне приемлемо требовать большего размера кучи.

Создание объектов в циклах - это не совсем плохая схема, и во многих случаях она весьма актуальна. Следует избегать повторного создания одного и того же объекта в цикле. Как правило, в циклах следует избегать конкатенации строк и заменять их на StringBuilder, созданный вне цикла, поскольку он намного менее эффективен с точки зрения производительности, но не с точки зрения памяти.

Не уверен, что я действительно отвечу на ваш вопрос.

...