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

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

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

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

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

Это правда?

Ответ 1

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

public class MyClass {

   public final MyOtherObject obj;

}

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

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

Ответ 4

Некоторые моменты для очистки:

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

  • Пока нет в Java.

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

Ответ 5

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

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

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

Ответ 6

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

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

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

final boolean debug = false;

......

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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

Статьи:

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

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

Ответ 9

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

Ответ 10

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

Ответ на ваш вопрос - подчеркнутый номер.

Ответ 11

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

Ответ 12

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

Ответ 13

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

Ответ 14

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

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

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

  

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