Неиспользуемый объект для сбора мусора, когда он все еще отображается в стеке?

В следующем примере есть два функционально эквивалентных метода:

public class Question {

    public static String method1() {
        String s = new String("s1");
        // some operations on s1
        s = new String("s2");
        return s;
    }

    public static String method2() {
        final String s1 = new String("s1");
        // some operations on s1
        final String s2 = new String("s2");
        return s2;
    }
}

однако в первом (method1) из них строка "s1" явно доступна для сбора мусора перед оператором return. Во второй (method2) строка "s1" по-прежнему доступна (хотя из обзора кода предполагается, что она больше не используется).

Мой вопрос: есть ли что-нибудь в jvm spec, которое говорит, что если переменная не используется в стеке, она может быть доступна для сбора мусора?

EDIT: Иногда переменные могут ссылаться на объект как полностью отображаемое изображение и влияют на память.

Я спрашиваю из-за практических соображений. У меня есть большой кусок памяти-жадного кода в одном методе и думаю, что если я смогу помочь JVM (немного), просто разделив этот метод на несколько небольших.

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

ОБНОВЛЕНИЕ: за jls-12.6.1:

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

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

Ответы

Ответ 1

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

Ответ 2

Если вы говорите об интерпретаторе, то во втором случае S1 остается "ссылкой" до тех пор, пока метод не выйдет, и кадр стека не будет свернут. (То есть в стандартном интерпретаторе вполне возможно, чтобы GC использовала информацию о жизнеспособности из проверки метода. И, кроме того (и, более вероятно), javac может на основе этого выполнить собственный анализ активности и "общий" интервал интерпретатора. )

В случае JITC, однако, даже слегка оптимизирующий может признать, что S1 не используется и перерабатывает этот регистр для S2. Или это не так. GC проверит содержимое регистра, и если S1 был повторно использован для чего-то другого, тогда старый объект S1 будет возвращен (если не указано иное). Если местоположение S1 не было повторно использовано, объект S1 не может быть восстановлен.

"Не могу", потому что, в зависимости от JVM, JITC может или не может предоставить GC карту, где ссылки на объекты "живут" в потоке программы. И эта карта, если она предоставлена, может или не может точно идентифицировать конец "живого диапазона" (последней точки отсчета) S1. Много разных возможностей.

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

Ответ 3

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

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

Что касается языка Java, то мусор может жить вечно без семантики программы воздействия. Вот почему JLS практически не говорит о мусоре вообще.

Ответ 4

в первой из них строка "s1" явно доступна для сбора мусора перед оператором return

Это не совсем понятно. Я думаю, вы путаете "неиспользованный" с "недостижимым". Они не обязательно одно и то же.

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

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

Ответ 5

В основном кадры стека и статическая область рассматриваются как корни GC. Таким образом, если объект ссылается на любой стек стека, он считается живым. Проблема с возвратом некоторых объектов из активного фрейма стека заключается в том, что GC работает параллельно с приложением (мутатором). Как вы думаете, что GC должен выяснить, что объект не используется во время выполнения метода? Для этого потребуется синхронизация, которая была бы ОЧЕНЬ тяжелой и сложной, на самом деле это нарушит идею о том, что GC будет работать параллельно с мутатором. Каждый поток может содержать переменные в регистрах процессора. Чтобы реализовать свою логику, они также должны быть добавлены в корни GC. Я даже не представляю, как его реализовать.

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

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