当前位置:  开发笔记 > 编程语言 > 正文

JVM GC是否可以在参考比较过程中移动对象,即使双方都引用同一个对象,也会导致比较失败?

如何解决《JVMGC是否可以在参考比较过程中移动对象,即使双方都引用同一个对象,也会导致比较失败?》经验,为你挑选了5个好方法。

众所周知,GC有时会在内存中移动对象.而且我的理解是,只要在移动对象时(在调用任何用户代码之前)更新所有引用,这应该是非常安全的.

但是,我看到有人提到参考比较可能是不安全的,因为GC在参考比较的中间移动了对象,这样即使两个引用都应该引用同一个对象,比较也可能失败?

即,是否存在以下代码不能打印"真实"的情况?

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

我试着谷歌搜索这个,缺乏可靠的结果让我相信说这个的人是错的,但我确实找到了各种各样的论坛帖子(比如这个)似乎表明他是正确的.但那个帖子也有人说不应该这样.



1> mhlz..:

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能够在执行字节码指令期间运行,对象的引用也不会改变.它在循环之前和之后仍然是相同的对象.

因此,简而言之,您的问题的答案是否定的,它将始终输出为真.


如果你引用了保证GC原子性的规范部分,这个答案会更好.
现代JVM不执行字节码,因此除了这一点之外.对于我所见过的每一个JVM,"对象的引用都不会改变"也是错误的(我认为没有任何JVM,其中对象的引用不是它的地址).
@Voo:即使现代JVM不执行字节码,它们也必须表现为_as if_它们执行字节代码.
一个有趣的论点.让我们把问题扩展到`if(foo == bar && foo == bar)`;-)
`GC运行的唯一时间是在两个字节码指令之间.但事实并非如此,因为a)任何规范都不能保证这一点和b)字节码在实践中不映射到硬件指令.你是从实现细节争论.它们对于这个问题并不重要,而且它们在时间上并不稳定.重要的是Java语言规范所说的内容.

2> Erik Nyström..:

资源:

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

简短的回答是,看看java 8规范:不.

所述==操作者将总是执行对象相等性检查(假定既没有参考为空).即使移动了对象,对象仍然是同一个对象.

如果你看到这样的效果,你刚刚发现了一个JVM错误.去提交吧.

当然,可能是JVM的某些模糊实现并没有因为任何奇怪的性能原因而强制执行此操作.如果是这种情况,那么从那个JVM继续前进是明智的......


,
.
我不知道§5.1.5是如何相关的.另一个参考是爆炸性的.从根本上说,`==`不是比较内存地址,而是比较对象的相等性.正如你所说的那样,一个JVM因为对象的地址发生变化而失败了`==`测试.:-)

3> home rrrrrrr..:

TL; DR

你不应该考虑那种东西,这是一个黑暗的地方.Java已经清楚地说明了它的规格,你不应该怀疑它.

2.7.对象的表示

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

资料来源:JVMS SE8.

我对此表示怀疑!如果你也许会怀疑这个非常基本的操作,你可能会发现自己怀疑一切,感到沮丧和偏执与信任问题是不是你想要去的地方.

如果它发生在我身上怎么办?不应该存在这样的错误.您提供的报告说,几年前发生的事情,不知怎么讨论OP决定弹出,截至没有理由,或者没有这样的错误的可靠的文件中的错误Oracle的讨论现在存在天.但是,如果您遇到此类错误或其他错误,请在此处提交.

为了让您的后顾之忧消失,Java已经将指针调整指针调整到JVM 指针表中,您可以在这里阅读更多关于它的效果.


@CompuChip太没了; 长读

4> the8472..:

GC只发生在程序中的状态定义良好的点上,并且JVM具有确切的知识,其中所有内容都在寄存器/堆栈中/堆上,因此所有引用都可以在对象移动时修复.

即,它们不能在任意汇编指令的执行之间发生.从概念上讲,你可以想到它们在JVM的字节码指令之间发生,GC调整了之前指令生成的所有引用.



5> Holger..:

你问一个错误前提的问题.由于==操作员不比较存储器位置,因此对存储器位置本身的改变是不明智的.无论JVM如何实现它==,应用于引用的运算符都会比较引用对象的标识.

举一个反映通常理解的例子,分布式JVM可能在不同计算机的RAM中保存对象,包括本地拷贝的可能性.所以简单地比较地址是行不通的.当然,由JVM实现来确保Java语言规范中定义的语义不会发生变化.

如果一个特定的JVM实现通过直接比较对象的内存位置来实现引用比较,并且有一个可以改变内存位置的垃圾收集器,当然,由JVM来确保这两个特性不会相互干扰.不相容的方式.

如果您对如何工作感到好奇,例如内部优化的JIT编译代码,那么粒度就不如您想象的那么精细.每个顺序代码(包括前向分支)都可以被认为运行得足够快,以便将垃圾收集延迟到完成.因此垃圾收集不能在优化代码中随时发生,但必须在某些点允许,例如

向后分支(请注意,由于循环展开,并非每个循环迭代都意味着向后分支)

内存分配

线程同步动作

调用尚未内联/分析的方法

也许是特别的东西,我忘记了

因此,JVM发出的代码包含已知的某些"安全点",当前持有哪些引用,如何更换它们,当然,更改位置对正确性没有影响.在这些点之间,代码可以运行而无需关心更改内存位置的可能性,而垃圾收集器将在必要时等待代码到达安全点,这保证在有限的,相当短的时间内发生.

但是,如上所述,这些是实施细节.在正式级别上,不存在更改内存位置等内容,因此无需明确指定不允许更改Java代码的语义.不允许执行细节.

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有