Использование final для переменных в Java улучшает сборку мусора?



сегодня у меня и моих коллег есть дискуссия об использовании final ключевое слово в Java для улучшения сборки мусора.



например, если вы пишете метод, как:



public Double doCalc(final Double value)
{
final Double maxWeight = 1000.0;
final Double totalWeight = maxWeight * value;
return totalWeight;
}


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



- Это правда?

556   14  

14 ответов:

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

public class MyClass {

   public final MyOtherObject obj;

}

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

JVM использует алгоритм GC mark-sweep, который должен исследовать все живые ссылки в местоположениях GC "root" (как и все объекты в текущий стек вызовов). Каждый живой объект "помечается" как живой, и любой объект, на который ссылается живой объект, также помечается как живой.

после завершения фазы метки GC просматривает кучу, освобождая память для всех немаркированных объектов (и уплотняя память для оставшихся живых объектов).

кроме того, важно признать, что память кучи Java разделена на "молодое поколение" и "старое поколение". Все объекты изначально выделяется в молодом поколении (иногда его называют "детским садом"). Поскольку большинство объектов недолговечны, ГК более агрессивно относится к освобождению недавнего мусора от молодого поколения. Если объект переживает цикл сбора данных молодого поколения, он перемещается в старое поколение (иногда называемое "арендованным поколением"), которое обрабатывается реже.

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

на мой взгляд, лучшая стратегия оптимизации управления памятью в Java-это как можно быстрее устранить ложные ссылки. Вы можете сделать это, назначив "null" ссылке на объект, как только вы закончите его использовать.

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

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

объявление локальной переменной final не повлияет на вывоз мусора, это только означает, что вы не можете изменить переменную. Ваш пример выше не должен компилироваться при изменении переменной totalWeight, который был отмечен final. С другой стороны, объявление примитива (double вместо Double)final will позволяет этой переменной быть встроенной в вызывающий код, так что это может привести к некоторому улучшению памяти и производительности. Это используется, когда у вас есть несколько public static final Strings в классе.

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

нет, это категорически не верно.

помните, что final не значит постоянно, это просто означает, что вы не можете изменить ссылку.

final MyObject o = new MyObject();
o.setValue("foo"); // Works just fine
o = new MyObject(); // Doesn't work.

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

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

некоторые моменты прояснить:

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

  • в Java пока нет распределения по стеку.

  • объявление переменной final означает, что вы не можете (при нормальных условиях) назначить новое значение этой переменной. Поскольку final ничего не говорит о масштабах, он не говорит ничего о его влияние на ГК.

Ну, я не знаю об использовании" окончательного " модификатора в этом случае или его влиянии на GC.

Но Я можете скажите вам следующее: ваше использование коробочных значений вместо примитивов (например, Double вместо double) выделит эти объекты в куче, а не в стеке, и создаст ненужный мусор, который GC должен будет очистить.

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

Final переменные не могут быть изменены после первоначального назначения (применяются компилятором).

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

вы должны знать, что final позволяет компилятору делать предположения о том, что оптимизировать. Встраивание кода и не включая код известно, что он недоступен.

final boolean debug = false;

......

if (debug) {
  System.out.println("DEBUG INFO!");
}

println не будет включен в байтовый код.

GC действует на недостижимые ссылки. Это не имеет ничего общего с "окончательным", которое является просто утверждением одноразового назначения. Возможно ли, что некоторые GC VM могут использовать "final"? Я не понимаю, как и почему.

есть не очень известный угловой случай с поколенческими сборщиками мусора. (Для краткого описания прочитайте ответ от benjismith для более глубокого понимания прочитайте статьи в конце).

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

теперь проблема возникает из-за того, что объект не может иметь ссылки на более молодые объекты. Когда долгоживущий объект (старое поколение) получает ссылку на новый объект, эта ссылка должна быть явно отслежена сборщиком мусора (см. статью от IBM на hotspot JVM collector), фактически влияющие на производительность GC.

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

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

статьи:

IBM на вывоз мусора: история, в hotspot JVM и производительность. Они могут больше не быть полностью действительными, поскольку они датируются 2003/04 годом, но они дают некоторое легкое для чтения представление о GCs.

Солнце настройки сборки мусора

final для локальных переменных и параметров не имеет никакого значения для создаваемых файлов классов, поэтому не может влиять на производительность среды выполнения. Если класс не имеет подклассов, HotSpot рассматривает этот класс как окончательный в любом случае (он может отменить позже, если класс, который нарушает это предположение, загружен). Я верю final на методы во многом то же самое, что и классы. final on static field может позволить переменной интерпретироваться как" константа времени компиляции", и оптимизация должна быть выполнена javac на этой основе. final на полях позволяет JVM некоторую свободу игнорировать происходит-перед отношения.

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

ответ на ваш вопрос-категорическое "нет".

все методы и переменные могут быть переопределены по умолчанию в подклассах.Если мы хотим сохранить подклассы от переопределения членов суперкласса, мы можем объявить их как final с помощью ключевого слова final. Например:- final int a=10; final void display(){......} Создание метода final гарантирует, что функциональность, определенная в суперклассе, никогда не будет изменена в любом случае. Аналогично, значение конечной переменной никогда не может быть изменено. Конечные переменные ведут себя как переменные класса.

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

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

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

  • Я есть чтобы сделать их окончательными, чтобы их можно было разделить с некоторым анонимным классом (например: создание потока демона и предоставление ему доступа к некоторому значению из метода enclosing)

  • Я хочу чтобы сделать их окончательными (например: некоторое значение, которое не должно/не переопределяется по ошибке)

Они помогают в быстрой сборке мусора?
AFAIK объект становится кандидатом на сборку GC, если он имеет нулевые сильные ссылки на него, и в этом случае также нет гарантии, что они будут немедленно собраны в мусор . В общем, сильная ссылка, как говорят, умирает, когда она выходит за пределы области или пользователь явно переназначает ее на нулевую ссылку, таким образом, объявление их окончательными означает, что ссылка будет продолжать существовать до тех пор, пока метод не существует (если его область действия явно сужается до определенного внутреннего блока {}), поскольку вы не можете переназначить конечные переменные. Поэтому я думаю, что сбор мусора w.r.t 'final' мая ввести нежелательную возможную задержку.

Comments

    Ничего не найдено.