Java堆只存储对象,栈只存储原始数据和对象引用。
考虑一下 A.a = B.b
,在哪里A.a
和B.b
在哪里int
。
在我的理解中,JVM会首先A.a
从堆中获取值到栈中,然后将堆上的值放入B.b
。似乎在堆上更改数据的唯一方法是从堆栈中放入值。
我的问题是:有没有办法在没有堆栈的 Java 堆上操作数据?A.a
例如,将直接的值复制到B.b
不进行堆栈操作。
如果你说“这取决于 JVM 的实现”,那么我的问题是关于 Dalvik。
Java堆只存储对象,栈只存储原始数据和对象引用。
考虑一下 A.a = B.b
,在哪里A.a
和B.b
在哪里int
。
在我的理解中,JVM会首先A.a
从堆中获取值到栈中,然后将堆上的值放入B.b
。似乎在堆上更改数据的唯一方法是从堆栈中放入值。
我的问题是:有没有办法在没有堆栈的 Java 堆上操作数据?A.a
例如,将直接的值复制到B.b
不进行堆栈操作。
如果你说“这取决于 JVM 的实现”,那么我的问题是关于 Dalvik。
至于称为 JVM 的抽象机(不要与同名的各种软件混淆,并通过将抽象机映射到真实硬件来实现该抽象机),A.a = B.b
确实将 的值加载B.b
到堆栈上,然后将其存储到A.a
. 1
然而,正如抽象机这个名字可能告诉你的那样,这只是一种思考语义的方式。只要保留程序的效果,实现就可以随心所欲。如您所知,大多数实现实际上并没有在大多数情况下解释 JVM 指令,而是将其编译为它们运行的 CPU 的机器代码。如果您担心性能或内存流量,则需要更深入。
编译时,JVM 的堆栈大部分被丢弃,以支持大多数物理 CPU 使用的寄存器。如果没有足够的可用寄存器,也可以使用硬件堆栈(与 JVM 堆栈不同!)。但我离题了。在大多数架构上,没有从一个内存位置移动到另一个内存位置的指令(请参阅汇编:在两个内存地址之间移动)。但是硬件栈也是内存,所以实际上不可能走heap -> stack -> heap。相反,您会发现代码将值从内存加载到寄存器中,然后将其从寄存器存储到内存中。
最后,如果对象 A 和 B 是短暂的并且没有别名,它们甚至可能被省略,它们的字段最终位于堆栈或寄存器中。然后,这个操作变得更加简单(如果没有效果,甚至可以完全删除)。
1这两个步骤实际上每个都需要几个 JVM 指令,但这并不重要。
在考虑 JIT 时,事情会变得复杂。我认为我的问题实际上是关于 Java 编译器,而不是 JVM
如果您正在考虑,javac
您应该假设它几乎没有进行任何优化,并且提供字节码几乎是代码的字面翻译,这是非常基于堆栈的。
事实上,字节码将比您的示例所建议的更多地使用堆栈。它执行类似的操作
push B
getAndPushField b
push A
popAndSetField a
即,理论上有 3 个,而不是一个堆栈操作。
另一方面,JIT 可能会对此进行优化,因此它甚至不使用寄存器来存储该值。取决于处理器
// R3 contains A, R7 contains B,
// a starts at the 14th byte
// b start at the 16th byte
MOVI [R3+14], [R7+16]
它不依赖于JVM的实现。它取决于 Java 虚拟机规范,除了通过堆栈之外没有提供任何其他方式。