64位计算机上的Long
对象可能需要24个字节:12个字节用于对象头,8个字节用于长值本身,另外4个字节用于填充。我很容易地在谷歌上搜索到了这个,但是我在JVM Specification中找不到。
我在任何地方都找不到的是,原始变量类型放在堆栈上时的大小是多少。它们到底有没有被填充?* 规则 * 是什么?例如,下面方法中的变量的堆栈内存占用量是多少:
class MemoryTest{
static void foo() {
int anInt=0;
long aLong=0L;
byte aByte=0;
short aShort=0;
// some code that uses the vars above
}
}
1条答案
按热度按时间mzaanser1#
我很容易在谷歌上搜索到这个,但是我在JVM规范中找不到它。
这是因为它是一个实现细节。规范没有说明的一切都是实现自己做自己事情的空间。在这个例子中,规范在Python 2.7中特别指出:
Java虚拟机不要求对象具有任何特定的内部结构。
JVM数据类型
JVM的数据类型在2.3中定义。
整数类型有:
byte
,其值为8位有符号二进制补码整数,默认值为零short
,其值为16位有符号二进制补码整数,默认值为零int
,其值为32位有符号二进制补码整数,默认值为零long
,其值为64位有符号二进制补码整数,默认值为零char
,其值为16比特不带负数号的整数,代表Basic Multilingual Plane中的Unicode字码指标,以UTF-16编码,预设值为null字码指标('\u0000')浮点类型包括:
float
,其值与32位IEEE 754 binary 32格式中可表示的值完全对应,并且其默认值为正零double
,其值与64位IEEE 754 binary 64格式的值完全对应,其默认值为正零它们只是具有您所期望的大小,没有像 Package 类那样的头。然而,JVM指令集是有限的,并不是每种类型都有对应的指令。实际上,大多数指令都是针对
int
、long
、float
double
。(更多信息请参见下表)因此,最终您几乎总是要使用这些类型。操作数堆栈和局部变量
注意,基本类型的值并不是“直接“放在堆栈上。JVM堆栈存储”帧“,这些帧“用于存储数据和部分结果,以及执行动态链接、返回方法的值和分派异常”。
在帧上,有一个“操作数堆栈”,其中存储部分结果。例如,一个实现可能会先将代码中的
0
压入操作数堆栈,然后再将它们弹出到局部变量中。该规范对操作数堆栈上的内容的大小进行了如下说明:
操作数堆栈上的每个条目都可以保存任何Java虚拟机类型的值,包括
long
或double
类型的值。[...]
在任何时间点,算子堆栈都有相关的深度,其中
long
或double
型别的值会贡献两个单位的深度,而任何其他型别的值则贡献一个单位的深度。根据规范,局部变量也存储在帧中的一个数组中。与您所请求的内容相关的部分是:
单个局部变量可以保存
boolean
、byte
、char
、short
、int
、float
、reference
或returnAddress
类型的值。一对局部变量可以保存long
或double
类型的值。[...]
类型
long
或类型double
的值占用两个连续的局部变量。这样的值只能使用较小的索引来寻址。例如,存储在局部变量数组中索引n处的类型double
的值实际上占用索引为n和n+1的局部变量;但是,位于索引n+1处的局部变量不能从加载。它可以存储到中。但是,这样做会使局部变量n的内容无效。Java虚拟机不要求n为偶数。直观地说,
long
和double
类型的值在局部变量数组中不需要64位对齐。实现者可以自由决定使用为该值保留的两个局部变量来表示这些值的适当方式。您的示例
对于您的代码,我们需要做一些合理的假设。让我们假设生成了下面的类文件(从
javap
看)。我使用javac -g MemoryTest.java
编译了这个文件。有趣的是,使用的所有指令都是int和long指令。没有
bipush
或sipush
,尽管编译器本可以使用它,但我猜这会使类文件略大。这就是我之前描述的“将0放在操作数堆栈上,然后将其弹出到局部变量”的行为。
如果我们还假设每个局部变量槽被实现为4个字节,则局部变量将总共占用20个字节。
anInt
、aByte
、aShort
每个占用一个槽,并且aLong
占用2个槽,因此总共占用5个槽。