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

Может ли JVM GC перемещать объекты в середине сравнения ссылок, что приводит к сбою сравнения, даже когда обе стороны ссылаются на один и тот же объект?

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

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

т.е. существует ли ситуация, при которой следующий код не будет печатать "истина"?

Foo foo = new Foo();
Foo bar = foo;
if(foo == bar) {
    System.out.println("true");
}

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

4b9b3361

Ответ 1

Инструкции Java Bytecode всегда являются атомарными относительно GC (т.е. ни один цикл не может произойти, пока выполняется одна команда).

Единственный раз, когда GC запускается, находится между двумя инструкциями Bytecode.

Глядя на байт-код, который генерирует javac для инструкции if в вашем коде, мы можем просто проверить, будет ли GC иметь какой-либо эффект:

// a GC here wouldn't change anything
ALOAD 1
// a GC cycle here would update all references accordingly, even the one on the stack
ALOAD 2
// same here. A GC cycle will update all references to the object on the stack
IF_ACMPNE L3
// this is the comparison of the two references. no cycle can happen while this comparison
// "is running" so there won't be any problems with this either

Кроме того, даже если GC удалось запустить во время выполнения инструкции байт-кода, ссылки на объект не изменились бы. Это все тот же объект до и после цикла.

Итак, короче, ответ на ваш вопрос - нет, он всегда будет выводить true.

Ответ 2

Источник:

https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.21.3

Короткий ответ, глядя на спецификацию java 8: Нет.

Оператор == всегда выполняет проверку равенства объектов (при условии, что ни одна ссылка не является нулевой). Даже если объект перемещен, объект остается тем же самым объектом.

Если вы видите такой эффект, вы только что обнаружили ошибку JVM. Пойдите, отправьте это.

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

Ответ 3

TL; DR

Вы не должны думать о таких вещах, что так всегда, Это темное место. Java четко изложила свои спецификации, и вы не должны сомневаться в этом.

2.7. Представление объектов

Виртуальная машина Java не предоставляет никакой конкретной внутренней структуры для объектов.

Источник: JVMS SE8.

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

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

Чтобы ваши заботы ушли, Java скорректировала указатель на подход указателя к таблице указателей JVM , вы можете узнать больше об этом эффективности здесь.

Ответ 4

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

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

Ответ 5

Вы задаете вопрос с неправильной предпосылкой. Поскольку оператор == не сравнивает ячейки памяти, это неразумно для изменения места памяти как такового. Оператор ==, применяемый к ссылкам, сравнивает идентичность упомянутых объектов, независимо от того, как JVM реализует его.

Чтобы назвать пример, который противодействует обычному пониманию, распределенная JVM может иметь объекты, хранящиеся в ОЗУ разных компьютеров, включая возможность локальных копий. Так что просто сравнение адресов не будет работать. Разумеется, его реализация JVM гарантирует, что семантика, определенная в Спецификации языка Java, не изменяется.

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

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

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

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

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

Ответ 6

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

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

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

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

Ответ 7

Нет, потому что это было бы грубо смешно и патентной ошибкой.

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

Ответ 8

Объект Java содержит ссылку на "объект" не на пространство памяти, где хранится объект.

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

В качестве примера для улучшения первый X int (я не помню, сколько) всегда выделяется в памяти для выполнения для цикла fatser (ex: for (int i =0; i<10; i++))

И как пример ссылки на объект, просто попробуйте создать и попытаться распечатать его

int[] i = {1,2,3};
System.out.println(i);

Вы увидите, что Java возвращает что-то, начинающееся с [[email protected]. Он говорит, что это точка в "массиве int at", а затем ссылка на объект. Не в зоне памяти!