Вопрос для интервью: объекты, подходящие для сбора мусора - PullRequest
18 голосов
/ 21 июля 2010

Дайте следующий код:

class A {
    Boolean b;
    A easyMethod(A a){
        a = null;
        return a;
    }
    public static void main(String [] args){
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a3 = a1.easyMethod(a2);
        a1 = null;
        // Some other code 
    }
}

Вопрос в том, сколько объектов может быть собрано для сбора мусора прямо перед // Some other code.

Тогда правильный ответ (по крайней мере, это интервьюерответ): 2 - логическое значение b, потому что это обертка и a1.

Не могли бы вы мне объяснить, почему a2 и a3 не являются мусоромсобрал?

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ:

  • Хорошо, я думаю, я понял это сейчас.Сначала это немного сбивало с толку, но теперь я уверен, что интервьюер ошибся.Моя первоначальная ошибка заключалась в том, что сначала я не считал, что Java передается только по значению, поэтому невозможно сделать a2 нулевым изнутри функции, которая принимает «a2» в качестве параметра, потому что этот a2 фактически является копией a2.
  • Часть с логическим значением b действительно была очевидна.

Спасибо за ответ, после этого я отправлю отзывы об интервью:).

Ответы [ 7 ]

19 голосов
/ 21 июля 2010

Предполагая, что go должно быть easyMethod, оно работает следующим образом

class A {
    Boolean b;
    A easyMethod(A a){
        a = null; // the reference to a2 was passed in, but is set to null
                  // a2 is not set to null - this copy of a reference is!
        return a; // null is returned
    }
    public static void main(String [] args){
        A a1 = new A(); // 1 obj
        A a2 = new A(); // 2 obj
        A a3 = new A(); // 3 obj
        a3 = a1.go(a2); // a3 set to null and flagged for GC - see above for why
        a1 = null; // so far, a1 and a3 have been set to null and flagged
        // Some other code 
    }
}

Два объекта имеют право на сборку мусора (a1 и a3). b не потому, что это только ссылка на ноль. Нет Boolean когда-либо было сделано.

Чтобы обойти безрассудные тонкости того, чем может быть // Some other code, я вместо этого задаю вопрос следующим образом:

Приведите и объясните следующий вывод:

class A {
    int i;
    A(int i) { this.i = i; }
    public String toString() { return ""+i; }
    A go(A a){
        a = null; // the reference to a2 was passed in, but is set to null
                  // a2 is not set to null - this copy of a reference is!
        return a; // null is returned
    }
    public static void main(String [] args){
        A a1 = new A(1); // 1 obj
        A a2 = new A(2); // 2 obj
        A a3 = new A(3); // 3 obj
        a3 = a1.go(a2); // a3 set to null and flagged for GC - see above for why
        a1 = null; // so far, a1 and a3 have been set to null and flagged

        test(a1);
        test(a2);
        test(a3);

    }
    static void test(A a) {
        try { System.out.println(a); } 
        catch(Exception e) { System.out.println((String)null); }
    }
}

И вывод:

c:\files\j>javac A.java

c:\files\j>java A
null
2
null

И следствием является то, что в тот момент a1 и a3 имели право на GC, а a2 - нет.

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

7 голосов
/ 21 июля 2010

Для оригинального референта a2 это фактически полностью зависит от того, что происходит в «некотором другом коде».Если «какой-то другой код» не использует a2 или a3, то исходный объект a2 пригоден для сборки мусора.

Это потому, что среде выполнения не нужно заботиться о лексической области видимости,Нужно просто знать, что на объект больше нельзя ссылаться.Поэтому, если «какой-то другой код» не использует a2 или a3, объект, на который они указывают , никогда не сможет снова ссылаться на , и поэтому он уже доступен для сборки мусора.

7 голосов
/ 21 июля 2010

При условии, что a1.go(a2) на самом деле означает a1.easyMethod(a2), ответ действительно 2, но не те, которые вы перечислили.Как правильно заметил Божо, b не инициализируется, поэтому не относится ни к какому объекту.Два объекта, подходящих для сбора мусора в точке комментария, являются объектами, на которые изначально ссылаются a1, и a3.

a1 явно обнуляется, а a3 переназначается для возврата.значение a1.easyMethod(a2), которое является нулевым.Однако на a2 вызов метода не влияет, так как Java передается по значению , поэтому в метод передается только копия ссылки a2.Даже если для копии задано нулевое значение, это не влияет на значение оригинала a2.

5 голосов
/ 21 июля 2010

Не могли бы вы мне объяснить, почему a2 и a3 не занимаются сборкой мусора?

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

5 голосов
/ 21 июля 2010

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

Неверно говорить о переменных , таких как b и a2, как о сборщике мусора. Объекты собирают мусор, а не переменные. Если переменная в области действия ссылается на объект, то он не может быть собран мусором. Проще говоря, только когда на объект больше не ссылается какая-либо переменная, он может быть сборщиком мусора.

Итак, у нас есть три случая создания A в этом коде. На них ссылаются a1 и т. Д., Но поскольку ссылки на переменные меняются, я буду ссылаться на экземпляры объекта как A1, A2 и A3. Поскольку вы не показали определение метода go, я собираюсь предположить, что это должен быть вызов easyMethod.

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

Поскольку переменная a2 никогда не переназначается (присваивание в easyMethod не влияет на исходную переменную), экземпляр A2 не может быть собран сборщиком мусора.

Поскольку easyMethod всегда возвращает null, а a3 присваивается результат этого метода, ничто не указывает на экземпляр A3, поэтому он также может собираться мусором.

3 голосов
/ 05 января 2013

Вопрос в том, сколько объектов может быть собрано для сбора мусора прямо перед // Some other code.

Вопрос бессмысленный.

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

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

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

Рассмотрим простую модель, в которой промежуточные значения (результаты всех подвыражений, а также переменные) помещаются в стек до конца функции. Простые компиляторы и виртуальные машины, такие как HLVM и .NET в Windows Phone 7, фактически работают на практике так, даже если это сохраняет ссылки дольше, чем необходимо. В этой модели каждый new A() помещает ссылку в стек до тех пор, пока функция не вернется, чтобы все три были доступны из стека в рассматриваемой точке и, следовательно, ничто не подходит для сбора мусора.

Рассмотрим модель, в которой ссылки удаляются из стека в первой точке, откуда они никогда больше не читаются. Это ближе к тому, как работают производственные виртуальные машины, такие как .NET и OCaml, за исключением того, что они по возможности сохраняют локальные ссылки в регистрах и перенаправляют на заранее выделенные записи в кадре стека вызова функции, перезаписывая мертвые локальные объекты, чтобы минимизировать размер кадра стека. В этой модели все ссылки мертвы в рассматриваемом вопросе, поэтому ни одна из них не достижима, и поэтому все три вновь выделенных объекта имеют право на сборку мусора, а также массив args и все строки, на которые он ссылается.

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

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

2 голосов
/ 21 июля 2010

Ваш вопрос довольно запутанный.

Сборщик мусора работает с объектами, а не с переменными.Поэтому, когда вы говорите, что a2 имеет право на GC, это ничего не значит.Вы должны сказать, что объект, на который ссылается a2 в строке N, подходит для GC в строке N + M.

В вашем примере у вас есть только 3 объекта, которые создаются.Это первый экземпляр A и последний экземпляр A, которые имеют право на сборщик мусора.

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