Java 如何识别一个位置是原始的还是引用的

How does Java identify if a location holds primitive or reference

如果堆上有一个对象,并且该对象有几个实例变量,一些是原始类型,一些是其他对象。那么,如果这个对象有 5 个字段,对象在内存中的结构是怎样的?具体来说……Java每个字段的数据类型存放在哪里?是否有一些'flag bytes'和一些'data bytes'其中'flag bytes'标识接下来几个'data bytes'的数据类型?

我指的是超出此答案的一些其他细节:

这个答案给出了更多关于数据本身如何存储在内存中的细节:

但它仍然没有说明标志的存储位置,表示数据类型为 int/long/double/float/reference。

这里有一个更具体的答案,恐怕还没有回答所有你的问题。这是 java 7 文档中的 link,相关部分是“2.11。指令集摘要”:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html

我会复制粘贴其中的一部分:

2.11.1。类型和 Java 虚拟机

Most of the instructions in the Java Virtual Machine instruction set encode type information about the operations they perform. For instance, the iload instruction (§iload) loads the contents of a local variable, which must be an int, onto the operand stack. The fload instruction (§fload) does the same with a float value. The two instructions may have identical implementations, but have distinct opcodes.

For the majority of typed instructions, the instruction type is represented explicitly in the opcode mnemonic by a letter: i for an int operation, l for long, s for short, b for byte, c for char, f for float, d for double, and a for reference.

2.11.2。加载和存储指令

The load and store instructions transfer values between the local variables (§2.6.1) and the operand stack (§2.6.2) of a Java Virtual Machine frame (§2.6).

Instructions that access fields of objects and elements of arrays (§2.11.5) also transfer data to and from the operand stack.

还有很多。读起来很有趣。

只有在编译时才需要类型信息来生成正确的字节码。字节码指令(如汇编指令)通常只能作用于一种数据类型。因此,所使用的指令反映了操作数的类型。大多数 C 系列语言都是如此。

要实际了解使用原始分配和动态分配时字节码有何不同,让我们举一个简单的例子。

public static void main (String [] args) {
    int i = 0;
    int j = i + 1;
}

并且生成的字节码:

public static void main(java.lang.String[]);
  Code:
     0: iconst_0
     1: istore_1
     2: iload_1
     3: iconst_1
     4: iadd
     5: istore_2
     6: return

所以我们使用 istoreiload 存储和加载整数,然后我们使用 iadd 添加它们(i 用于整数)。

现在以这个例子为例,使用动态内存分配而不是原语:

public static void main (String [] args) {
    Integer i = new Integer(0);
    int j = i + 1;
}

和字节码:

public static void main(java.lang.String[]);
  Code:
     0: new           #2                  // class java/lang/Integer
     3: dup
     4: iconst_0
     5: invokespecial #3                  // Method java/lang/Integer."<init>":(I)V
     8: astore_1
     9: aload_1
    10: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
    13: iconst_1
    14: iadd
    15: istore_2
    16: return

在这个版本中,我们首先必须调用Integer对象的intValue()方法来获取值,然后我们可以通过iadd对其进行操作。

关于编译后不需要存储数据类型的证据(因为它们在指令本身中编码,例如 istore 代表 "integer store"),请参阅 jrahhali 的回答中的参考资料。