Подтвердить что ты не робот

Каковы некоторые примеры практики управления памятью Java?

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

Я начал пробираться и делать некоторые очистки. Но чем больше я переживал, тем больше вопросов у меня было "будет ли это на самом деле что-нибудь делать?"

Например, вместо объявления переменной вне цикла, упомянутой выше, и просто установки ее значения в цикле... они создали объект в цикле. Я имею в виду:

for(int i=0; i < arrayOfStuff.size(); i++) {
    String something = (String) arrayOfStuff.get(i);
    ...
}

против

String something = null;
for(int i=0; i < arrayOfStuff.size(); i++) {
    something = (String) arrayOfStuff.get(i);
}

Неправильно ли говорить, что нижний цикл лучше? Возможно, я ошибаюсь.

Кроме того, как насчет второго цикла выше, я вернул "что-то" в нуль? Будет ли это очищать память?

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

Update:

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

4b9b3361

Ответ 1

Не пытайтесь перехитрить виртуальную машину. Первый цикл - предлагаемая наилучшая практика, как для производительности, так и для обслуживания. Установка ссылки обратно на нуль после того, как цикл не гарантирует немедленного освобождения памяти. GC выполнит свою работу лучше всего, когда вы воспользуетесь минимальной возможной областью.

Книги, которые подробно описывают эти вещи (с точки зрения пользователя), Эффективная Java 2 и Шаблоны реализации.

Если вы хотите узнать больше о производительности и внутренних клиентах, вам нужно поговорить или прочитать книги из Brian Goetz.

Ответ 2

Эти две петли эквивалентны, за исключением области something; см. этот вопрос для деталей.

Общие рекомендации? Умм, давайте посмотрим: не храните большие объемы данных в статических переменных, если у вас нет веской причины. Удалите большие объекты из коллекций, когда закончите с ними. И да, "Мера, не догадайтесь". Используйте профилировщик, чтобы узнать, где выделяется память.

Ответ 3

В обоих образцах кода объектов нет. Вы просто устанавливаете ссылку на объект на строку, которая уже находится в arrayOfStuff. Так что в памяти нет разницы.

Ответ 4

Две петли будут использовать в основном одинаковый объем памяти, любая разница будет незначительной. "String something" только создает ссылку на объект, а не новый объект сам по себе, и поэтому любая используемая дополнительная память невелика. Кроме того, компилятор/в сочетании с JVM, скорее всего, оптимизирует сгенерированный код.

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

Вы также можете посмотреть "Слабые ссылки" и другие специализированные классы управления памятью.

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

Обновление. Ключом к управлению памятью являются структуры данных, а также то, какую производительность вам нужно/когда. Компромисс часто происходит между циклами памяти и процессора.

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

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

Ответ 5

JVM лучше всего освобождает короткоживущие объекты. Старайтесь не выделять объекты, которые вам не нужны. Но вы не можете оптимизировать использование памяти, пока не поймете свою рабочую нагрузку, время жизни объекта и размеры объекта. Профилировщик может сказать вам это.

Наконец, # 1 вещь, которую вы должны избегать: никогда не используйте Finalizers. Финализаторы вмешиваются в сбор мусора, поскольку объект не может быть просто освобожден, но должен быть поставлен в очередь для завершения, что может произойти или не произойти. Лучше никогда не использовать финализаторы.

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

Ответ 6

Первый цикл лучше. Поскольку

  • переменная будет быстрее (теоретическая).
  • лучше читать программу.

Но из точки памяти это не имеет значения.

Если у вас проблемы с памятью, вы должны профиль, где он потребляется.

Ответ 7

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

Ваше приложение, вероятно, имеет несколько центральных структур данных. Это те, о которых вы должны беспокоиться. Например, если вы их заполняете, предварительно распределите их с хорошей оценкой размера, чтобы избежать повторного изменения размера базовой структуры. Это особенно относится к StringBuffer, ArrayList, HashMap и тому подобное. Настройте свой доступ к этим структурам хорошо, поэтому вам не нужно много копировать.

Используйте соответствующие алгоритмы для доступа к структурам данных. На самом низком уровне, например, в цикле, который вы упомянули, используйте Iterator s или, по крайней мере, избегайте вызова .size() все время. (Да, вы каждый раз спрашиваете список за этот размер, который большую часть времени не меняется.) Кстати, я часто видел аналогичную ошибку с Map s. Люди перебирают значения keySet() и get для каждого значения, вместо того, чтобы просто перебирать entrySet() в первую очередь. Менеджер памяти поблагодарит вас за дополнительные циклы процессора.

Ответ 8

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

В дополнение к этому есть еще одно преимущество. Вы больше узнаете о своем языке программирования и своем приложении.

Я использую VisualVM для профилирования и рекомендую его очень. Он поставляется с дистрибутивом jdk/jre.

Ответ 9

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

Большинство проблем с памятью Java возникают при хранении объектов в коллекции, но забывайте их удалить. В противном случае GC делает свою работу неплохо.

Ответ 10

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

Причина в том, что все, что является "выделенным", является ссылкой, которая является 4 байтовой переменной стека (в большинстве 32-разрядных систем). Переменная стека "распределяется" путем добавления к адресу памяти, представляющему верхнюю часть стека, и поэтому очень быстрая и дешевая.

Что нужно, чтобы быть осторожным, это для циклов вроде:

for (int i = 0; i < some_large_num; i++)
{
   String something = new String();
   //do stuff with something
}

поскольку на самом деле выполняется выделение памяти.

Ответ 12

"Я неверно сказать, что нижний цикл лучше?", ответ НЕТ, не только лучше, в том же случае необходимо... Определение переменной (а не контент) сделано в куче памяти, и ограничено, в первом примере каждый цикл создает экземпляр в этой памяти, и если размер "arrayOfStuff" большой, может возникнуть ошибка "Ошибка вне памяти: пустое пространство Java"....

Ответ 13

Из того, что я понимаю, вы смотрите, нижняя петля не лучше. Причина в том, что даже если вы пытаетесь повторно использовать одну ссылку (Ex-something), факт - это объект (Ex - arrayOfStuff.get(i)) по-прежнему ссылается на List (arrayOfStuff). Чтобы объекты, имеющие право на сбор, не должны ссылаться ни с одного места. Если вы уверены в жизни списка после этого момента, вы можете решить удалить/освободить от него объекты в отдельном цикле.

Оптимизация, которая может быть выполнена со статической точки зрения (т.е. никаких изменений не происходит с этим списком из любого другого потока), лучше избегать многократного вызова. То есть, если вы не ожидаете изменения размера, то зачем его рассчитывать снова и снова; в конце концов это не array.length, это list.size().