64位计算机上的Long对象可能需要24个字节:对象头为12字节,长值本身为8个,填充为另外4个字节。我很容易就搜索到了这个,但是我在JVM规范中找不到它。
我在任何地方都找不到的是,当放在堆栈中时,原始变量类型大小是什么。他们有垫子吗?规则是什么?例如,以下方法中变量的堆栈内存占用量是多少:
class MemoryTest{
static void foo() {
int anInt=0;
long aLong=0L;
byte aByte=0;
short aShort=0;
// some code that uses the vars above
}
}发布于 2022-09-24 12:10:14
我很容易在googled上搜索到,但是在JVM规范中找不到它。
那是因为它是一个实现细节。规范中没有提到的所有东西都有空间让实现做自己的事情。在本例中,规范在2.7中专门调用了这一点。
Java虚拟机不要求对象的任何特定内部结构。
JVM数据类型
JVM的数据类型是在2.3中定义的。
整体类型如下:
byte,其值为8位带符号的两个补整数,默认值为零。short,其值为16位带符号的两个补整数,默认值为零。int,其值为32位带符号的两个补整数,默认值为零。long,其值为64位带符号的两个补整数,默认值为零。char,其值为16位无符号整数,表示基本多语言平面上的Unicode代码点,用UTF-16编码,默认值为空代码点('\u0000')。浮点类型包括:
float,其值与32位IEEE 754 binary32格式中可表示的值完全对应,其默认值为正零。double,其值与64位IEEE 754 binary64格式的值完全对应,其默认值为正零。它们只有您所期望的大小,没有像包装类那样的标题。但是,JVM指令集是有限的,并不是每种类型的指令都有。实际上,大多数指令都是针对int、long、float、double的。(参见这张表可以获得更多信息。)因此,最终您几乎总是在使用这些类型。
操作数堆栈和局部变量
注意,基本类型的值不是直接“放到堆栈上”的。JVM 堆栈存储https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-2.html#jvms-2.6,它“用于存储数据和部分结果,以及执行动态链接、返回方法的值和分派异常”。
在框架上,有一个“操作数堆栈”,其中存储部分结果。例如,实现可能首先将代码中的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使用javac -g MemoryTest.java编译了这个程序。
Classfile /Users/mulangsu/Desktop/MemoryTest.class
Last modified 2022/09/24; size 264 bytes
SHA-256 checksum c1aa63404c5e590ef52116de586a91d75deb8727ab749cbd1e0d6fb4197357c2
Compiled from "MemoryTest.java"
class MemoryTest
minor version: 0
major version: 61
flags: (0x0020) ACC_SUPER
this_class: #7 // MemoryTest
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // MemoryTest
#8 = Utf8 MemoryTest
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 foo
#12 = Utf8 SourceFile
#13 = Utf8 MemoryTest.java
{
MemoryTest();
descriptor: ()V
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
static void foo();
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=5, args_size=0
0: iconst_0
1: istore_0
2: lconst_0
3: lstore_1
4: iconst_0
5: istore_3
6: iconst_0
7: istore 4
9: return
LineNumberTable:
line 3: 0
line 4: 2
line 5: 4
line 6: 6
line 8: 9
LocalVariableTable:
Start Length Slot Name Signature
2 8 0 anInt I
4 6 1 aLong J
6 4 3 aByte B
9 1 4 aShort S
}
SourceFile: "MemoryTest.java"有趣的是,所有使用的指令都是int和长指令。没有bipush或sipush,即使编译器可以使用它,但我想这会使类文件稍微大一些。
这是我前面描述的“在操作数堆栈上放置0,然后弹出它到局部变量”的行为。
如果我们还假设每个局部变量槽被实现为4个字节,那么局部变量总共将占据20个字节。anInt、aByte、aShort各占一个插槽,aLong占2个,因此总共有5个插槽。
https://stackoverflow.com/questions/73836807
复制相似问题