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

Помогает ли GC скрыть локальные переменные в Java?

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

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

Мой вопрос: откуда этот миф о помощи GC? (Я упоминался в "Книгах памяти 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
  }
}
4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

Итак, вы правы, что это не важно.

Ответ 3

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

Ответ 4

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

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

Ответ 5

Насколько я знаю, 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();
}

Ответ 6

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

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

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

EDIT: добавьте ссылку на Effective Java 2nd Edition на веб-сайте Sun.

Ответ 7

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

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 Virtual Machine не знает о таких областях. Все, что у него есть:

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)

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

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

Ответ 8

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

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

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

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

Я не знаю технических деталей, но, насколько я помню, переменная является не чем иным, как ссылкой из текущего кадра стека, и пока эта ссылка не будет удалена, объект не может быть собран в мусор. Теперь, но явно устанавливая его равным null, вы убедились, что ссылка ушла. Если вы этого не сделаете, вы в основном разрешаете виртуальной машине решить, когда эта ссылка будет очищена, что может быть или не быть при выходе из области (в отличие от С++, если объект находится в стеке и ДОЛЖЕН быть уничтожен). Возможно, когда стоп-фрейм будет перезаписан следующим. Я не уверен, есть ли там виртуальная машина, которая делает это.

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

Ответ 12

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

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

Ответ 13

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

Это почти никогда не проблема, чаще всего обнуление объектов не является полезным.

Когда вы думаете о распределении и освобождении объекта, обратите внимание на то, что "Система" имеет в виду: Активные потоки, окна, которые не были утилизированы() d, и еще одна или две вещи, но я могу " t помню прямо сейчас.

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

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

(эти триммы, кстати, довольно эффективны, даже больше, чем С++ бесплатно, поскольку они не требуют касания каждого объекта по мере его освобождения)

Ответ 14

Если вам больше не нужны большие объекты в локальной области, вы можете дать JVM подсказку и установить ссылочный NULL.

public void foobar()
{
    List<SomeObject> dataList = new ArrayList<SomeObject>();

    // heavy computation here where objects are added to dataList 
    // and the list grows, maybe you will sort the list and
    // you just need the first element... 

    SomeObject smallest = dataList.get(0);

    // more heavy computation will follow, where dataList is never used again
    // so give the JVM a hint to drop it on its on discretion
    dataList = null;

    // ok, do your stuff other heavy stuff here... maybe you even need the 
    // memory that was occupied by dataList before... 

    // end of game and the JVM will take care of everything, no hints needed
}

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

Ответ 15

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