Java JVM GC是否可以在引用比较的中间移动对象,甚至在双方引用同一对象时导致比较失败?

Java JVM GC是否可以在引用比较的中间移动对象,甚至在双方引用同一对象时导致比较失败?,java,garbage-collection,jvm,Java,Garbage Collection,Jvm,众所周知,地面军事系统有时会在内存中移动对象。据我所知,只要在移动对象时(在调用任何用户代码之前)更新所有引用,这应该是完全安全的 但是,我看到有人提到,引用比较可能是不安全的,因为在引用比较的中间,对象被GC移动,即使当两个引用都引用同一个对象时,比较可能失败, 即,是否存在以下代码不会打印“true”的情况 我试着用谷歌搜索这篇文章,由于缺乏可靠的结果,我相信说这篇文章的人是错的,但我确实找到了各种各样的论坛帖子()似乎表明他是正确的。但是这个线程也有人说不应该是这样的。Java对象持有对“

众所周知,地面军事系统有时会在内存中移动对象。据我所知,只要在移动对象时(在调用任何用户代码之前)更新所有引用,这应该是完全安全的

<>但是,我看到有人提到,引用比较可能是不安全的,因为在引用比较的中间,对象被GC移动,即使当两个引用都引用同一个对象时,比较可能失败,

即,是否存在以下代码不会打印“true”的情况


我试着用谷歌搜索这篇文章,由于缺乏可靠的结果,我相信说这篇文章的人是错的,但我确实找到了各种各样的论坛帖子()似乎表明他是正确的。但是这个线程也有人说不应该是这样的。

Java对象持有对“对象”的引用,而不是对存储对象的内存空间的引用

Java这样做是因为它允许JVM自己管理内存使用(例如垃圾收集器),并在不直接影响客户端程序的情况下提高全局使用率

作为改进的例子,第一个X int(我不记得有多少)总是在内存中分配,以执行for循环fatser(例如:
for(int I=0;ISource:

简单的回答是,看看Java8规范:否

=
操作符将始终执行对象相等性检查(假设两个引用都不为空)。即使对象被移动,对象仍然是同一个对象

如果您看到这样的效果,您刚刚发现了一个JVM bug。请提交它


当然,可能是由于某种奇怪的性能原因,JVM的一些模糊实现没有强制执行这一点。如果是这种情况,那么明智的做法是简单地从JVM开始…

GCs只发生在程序中状态定义良好且JVM确切知道所有内容在寄存器中的位置的点上s/将堆栈/放在堆上,以便在对象移动时可以修复所有引用


也就是说,它们不能发生在任意汇编指令的执行之间。从概念上讲,您可以认为它们发生在JVM的字节码指令之间,GC调整以前的指令生成的所有引用。

Java字节码指令相对于GC始终是原子的(即,在执行单个指令时不会发生循环)

GC运行的唯一时间是在两条字节码指令之间

查看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。

TL;DR 你不应该去想那些东西,那是一个黑暗的地方。 Java已经清楚地说明了它的规范,您永远不应该怀疑它

2.7.对象的表示

Java虚拟机不要求对象具有任何特定的内部结构

资料来源:

我怀疑它!如果你怀疑这个非常基本的操作员,你可能会发现自己怀疑其他一切,对信任问题感到沮丧和偏执不是你想要的地方

如果它发生在我身上怎么办?这样的错误不应该存在。您提供的Oracle讨论报告了一个多年前发生的错误,不知何故,讨论OP决定无缘无故地弹出该错误,也就是说现在还没有此类错误的可靠文档。但是,如果您遇到此类错误或任何其他错误,请提交它


为了消除您的担忧,Java已经将指针对指针的方法调整到JVM中,您可以阅读更多关于它的效率的信息。

我知道您是在有人说它是这样做的之后问这个问题的,但真正问它是否确实是这样做的并不是评估他们所说内容的正确方法

您真正应该问的是(主要是您自己,只有在您无法决定答案的情况下,其他人才会问)允许GC导致逻辑上应该成功的比较失败(基本上是任何不包含弱引用的比较)是否有意义

答案显然是“不”,因为它将打破除“你好,世界”之外的几乎任何东西,甚至可能是那样


因此,如果允许的话,它是一个bug——无论是在规范中还是在实现中。现在,由于规范和实现都是由人类编写的,所以这种bug是可能存在的。如果是这样,它将被报告并几乎肯定会被修复。

您提出的问题的前提是错误的。因为
=
操作符不比较内存对于y个位置,内存位置本身的更改是不明智的。应用于引用的
=
操作符比较引用对象的标识,而不管JVM如何实现它

举一个与通常理解相反的例子,分布式JVM可能在不同计算机的RAM中保存对象,包括本地副本的可能性。因此简单地比较地址是行不通的。当然,要确保Java语言规范中定义的语义,不要改变

如果一个特定的JVM实现通过直接比较对象的内存位置来实现引用比较,并且有一个可以更改内存位置的垃圾收集器,那么当然,这取决于JVM来完成
int[] i = {1,2,3};
System.out.println(i);
// 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