Поможет ли GC обнулить локальные переменные в Java? - PullRequest
40 голосов
/ 23 января 2009

Я был вынужден добавить оператор myLocalVar = null; в предложение finally перед выходом из метода. Причина в том, чтобы помочь GC. Мне сказали, что я получу SMS-сообщения ночью, когда сервер в следующий раз выйдет из строя, поэтому я лучше сделал это: -).

Я думаю, что это бессмысленно, так как myLocalVar находится в области действия метода и будет «потерян» при выходе из метода. Дополнительный обнуление просто загрязняет код, но безвреден.

Мой вопрос: откуда взялся этот миф о помощи ГК? (Меня называли «книгами памяти на Java»). Знаете ли вы какую-нибудь статью «авторитетов», которая объясняет это более подробно? Есть ли вероятность, что это не миф, но действительно как-то помогает? Если так, то как? Может ли обнуление локальных переменных причинить какой-либо вред?

Чтобы уточнить, метод выглядит следующим образом:

void method() {
  MyClass myLocalVar = null;
  try {
    myLocalVar = get reference to object;
    ... do more here ...
  } finally {
    if (myLocalVar != null) {
      myLocalVar.close(); // it is resource which we should close
    }

    myLocalVar = null; // THIS IS THE LINE I AM TALKING ABOUT
  }
}

Ответы [ 15 ]

28 голосов
/ 02 февраля 2009

Был старый фрагмент документации Sun, Производительность платформы Java (ссылка, к сожалению, теперь не работает, и я не смог найти новую), в которой описывался Ситуация, когда обнуление локальной переменной, выпадающей из области действия, фактически оказало влияние на сборщик мусора.

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

19 голосов
/ 23 января 2009

Java GC должен быть "звуковым", но не обязательно сразу "завершенным". Другими словами, он спроектирован так, чтобы никогда не удалять объекты, которые по-прежнему доступны по крайней мере по одному пути (и, следовательно, вызывать висячую ссылку). Оно не обязательно должно быть немедленно завершено, поскольку может потребоваться время, чтобы удалить все, что можно удалить.

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

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

Наконец, есть люди, которые чрезмерно работают в GC и думают, что он сделает для них функциональное отключение (например, закроет соединения, когда переменная удалена), что, конечно, не так. Я думаю, что источником этой строки является «Я действительно покончил с этим, но я не уверен, как это обеспечить».

Так что, да, вы правы, что это ненужно.

9 голосов
/ 23 января 2009

Не в этом случае. myLocalVar выходит из области видимости, как только функция возвращается, поэтому установка ссылки на null абсолютно ничего не делает.

8 голосов
/ 23 января 2009

Это миф, который восходит к тому времени, когда впервые появилась Java, и ребята из C ++ не доверяли gc.

ГС знает, что делает. обнуление var ничего не обидит, но и ничего не поможет. У Джеффа на этот раз был довольно забавный пост .

5 голосов
/ 23 января 2009

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

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

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

SomeResult SomeFunction(SomeClass param) {
    TempData big = new TempData(param);
    IntermediateResult intermediate = big.GetIntermediateResult();
    big = null; // allow GC to reclaim the memory before returning from the function
    intermediate.FurtherProcessing();
    return intermediate.EvenMoreProcessing();
}
3 голосов
/ 24 февраля 2009

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

public class Main {
    public static void main(String[] args) {
       {
           Main local = new Main();

           // inner = null;
       }

       while (true) {
           // long running method
       }
    }
}

Если inner = null; закомментирован, объект в переменной local не может быть удален во время цикла while. Причина в том, что виртуальная машина Java не знает о таких областях, как эта. Все, что у него есть:

D:\workspaces\workspace-3.4\test\src>javap -verbose -c Main
public class Main extends java.lang.Object
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #4.#11; //  java/lang/Object."<init>":()V
const #2 = class        #12;    //  Main
const #3 = Method       #2.#11; //  Main."<init>":()V
const #4 = class        #13;    //  java/lang/Object
const #5 = Asciz        <init>;
const #6 = Asciz        ()V;
const #7 = Asciz        Code;
const #8 = Asciz        main;
const #9 = Asciz        ([Ljava/lang/String;)V;
const #10 = Asciz       StackMapTable;
const #11 = NameAndType #5:#6;//  "<init>":()V
const #12 = Asciz       Main;
const #13 = Asciz       java/lang/Object;

{
public Main();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=2, Args_size=1
   0:   new     #2; //class Main
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   goto    8
  StackMapTable: number_of_entries = 1
   frame_type = 8 /* same */

}

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

public class Main
{

    public Main() { }

    public static void main(String args[])
    {
        Main main1 = new Main();
        do
            ;
        while(true);
    }
}

(Генерируется декомпилятором JAD)

Вывод: в некоторых особых случаях, подобных этому, есть некоторое обоснование в обнулении локальных переменных. Но если метод скоро закончится (как в моем первоначальном вопросе), это не поможет.

Это было вдохновлено комментарием Зденек Троничек в списке рассылки java-cz (на чешском языке, извините)

3 голосов
/ 23 января 2009

Вы правы. Обнуление переменной, которая в любом случае немедленно выйдет из области видимости, не нужно и не имеет никакого значения для GC. Все, что он делает, это загромождает код. В Effective Java 2nd Edition автор рекомендует избегать ненужного обнуления локальных переменных. См. Эффективная Java, 2-е издание, пункт 6: устранение устаревших ссылок на объекты для полной записи.

Это также можно увидеть в статье Создание и уничтожение объектов Java в InformIT. Прочитайте всю статью , чтобы найти место, где Джошуа Блох соглашается с вами.

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

РЕДАКТИРОВАТЬ: Добавить ссылку на Effective Java 2nd Edition на веб-сайте Sun

2 голосов
/ 23 января 2009

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

Вернемся к JDK1.3. У меня действительно был случай с очень большим графом объектов, который также содержал множество циклических ссылок в графе. Удаление некоторых ссылок действительно заметно улучшило время GC.

Я не уверен, применимо ли это к современной виртуальной машине. Сборщики мусора становятся все умнее.

1 голос
/ 24 января 2009

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

  • В модульных тестах, которые создают большой объект в поле. Тестер модулей может сохранить тестовый объект и объекты, которые вы создали, на время всех тестов. Это может привести к нехватке памяти у тестера. В этом случае часто лучше использовать локальные переменные вместо полей, но если поле требуется, его можно очистить в tearDown.
  • Циркулярные ссылки могут быть очищены GC, но не с помощью простого инкрементального сбора. Это может означать, что объектам с циклическими ссылками требуется больше работы для очистки и они могут жить намного дольше, чем в противном случае. Как правило, это не имеет значения, но если вы пытаетесь сократить время, затрачиваемое на сборку мусора, это может помочь сломать циклические ссылки.
1 голос
/ 23 января 2009

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

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

...