Java堆只存储对象,而堆栈只存储原始数据和对象引用。
考虑 A.a = B.b
,其中A.a
和B.b
为int
。
根据我的理解,JVM将首先从堆中获取A.a
的值到堆栈,然后将值PUT到堆上的B.b
。似乎唯一的方法来更改堆上的数据就是从堆栈中输出值。
我的问题是:有没有办法在没有堆栈的情况下在Java堆上运行数据?例如,将A.a
的值直接复制到B.b
而不进行堆栈操作。
如果你说“这取决于JVM的实现”,那么我的问题是关于Dalvik。
答案 0 :(得分:2)
对于名为JVM的抽象机器(不要与通过将其映射到真实硬件上的相同名称和实现抽象机器的各种软件混淆)而言,A.a = B.b
确实在堆栈上加载B.b
的值,然后将其存储到A.a
。 1
然而,正如名称 abstract 机器可能会告诉您的,这只是一种思考语义的方式。只要保留程序的效果,实现可以随心所欲。正如您应该知道的,大多数实现在大多数情况下并不实际解释JVM指令,而是将其编译为运行它们的CPU的机器代码。如果你担心性能或内存流量,你需要更深入。
编译时,JVM的堆栈大部分被丢弃,转而使用大多数物理CPU使用的寄存器。如果没有足够的寄存器,也可以使用硬件堆栈(与JVM堆栈不同!)。但我离题了。在大多数体系结构中,没有从一个内存位置移动到另一个内存位置的指令(请参阅Assembly: MOVing between two memory addresses)。但是,硬件堆栈也是内存,所以它实际上不可能去堆 - >堆栈 - >堆。相反,您会发现代码将内存中的值加载到寄存器中,然后将其从寄存器存储到内存中。
最后,如果对象A和B是短暂的并且没有别名,它们甚至可能被删除,其字段最终在堆栈上或寄存器中。然后,这个操作变得更简单(或者如果没有效果,甚至可以完全删除)。
1 这两个步骤实际上每个都有几个JVM指令,但这里并不重要。
答案 1 :(得分:1)
在考虑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]
答案 2 :(得分:0)
它不依赖于JVM的实现。它取决于Java虚拟机规范,它不提供任何其他方式,而不是通过堆栈。