Java原始类型变量存储

Posted

技术标签:

【中文标题】Java原始类型变量存储【英文标题】:Java primitive type variable storage 【发布时间】:2021-08-01 06:13:54 【问题描述】:

我了解对象变量存储引用(内存地址)和原始变量类型存储值,但原始变量是否仍然需要内存地址来定位值?因此,原始类型变量是否实际上将 ref(mem addr)存储到值?

ie int num = 10; //num是10在内存中的位置?

【问题讨论】:

任何变量都必须在内存中有一个位置来存储它的值,是的。 有关更多信息,您可能想了解 java 如何使用堆栈和堆:journaldev.com/4098/java-heap-space-vs-stack-memory 【参考方案1】:
ie int num = 10; //num is the location of the 10 in memory?

实际上,它在堆栈中。

至少,局部变量和参数是。所以,在:

public void foo() 
   int x = 10;
   System.out.println(x);
   int y = 20;
   System.out.println(y);

会发生什么(编译上面的代码,然后运行javap -c 来检查字节码并继续),javac 会将它编译成如下所示的字节码:

STARTMETHOD foo SLOTS=1
LOADC 0, 10 # load constant value '10' into the first slot (slot 0)
PUSH # Push this on the stack.
INVOKESTATIC java/lang/System println(int)
LOADC 0, 20 # load constant value '20' into... the first slot!
PUSH
INVOKESTATIC java/lang/System println(int)

注意:这过于简单化了; System.out.println 实际上是一个GETFIELD,然后是一个接口调用结果,以及更多这样的简化。但是与栈和槽相关的位代表了它是如何工作的。

你在这里看到了一些有趣的东西。值得注意的是,局部变量与内存位置不是一对一的匹配。 Javac 意识到当你创建 y 时你从不使用 x,所以它只是重复使用同一个 slot。

那个“插槽”的东西被声明在堆栈上。堆栈是内存,但它不断被重用。堆栈是一小块内存(想想 1MB 左右),每次调用一个方法时,你来自哪里,它拥有的所有状态(本地变量等)都存在那里,所有新状态(新方法的本地变量,例如这些插槽)被放在“顶部”。然后当你从一个方法返回时,那个“指向栈顶的指针”正好回到它原来的位置,所以你调用的下一个方法会立即覆盖这些东西。

那么,num 是“内存地址”吗?不是真的,因为地址实际上是“0”(第一个插槽)。这不会转化为“我们堆栈上的第 0 个项目”。它转换为“进入此方法时的堆栈顶部”,将其称为“内存位置”有点牵强。

在任何情况下,这个变量都不存在于堆上根本 - 堆栈与堆是分开的。

对于字段,情况有所不同。给定:

class Foo 
   int num = 10;
   Object o = new Object();

这是完全不同的。 Foo 的实例由一堆堆上的内存表示。具体来说,一些字节来注册这是一个 Foo 实例,然后是一些字节来存储“10”(字面意思是,0x00 00 00 10 显示在内存中,可能是 0x10 00 00 00,具体取决于事物的字节序 - 如果你 coredump 进程内存空间),然后是一些字节来存储一个 'ref' 到那个对象 o。

对该字段的引用永远不会存储为内存地址。它存储为此 Foo 实例的“引用”,VM 将“获取num 字段的值”翻译为“相对于实例所在位置的第 10 个偏移量”。

如果您想将其称为内存地址,请成为我的客人。但是这样规定有什么意义呢?

'ref' 就像一个指针,除了它通常不是直接的内存地址,而是 JVM 可以用来确定内存地址的某个数字。通常对象在 8 字节边界上对齐,因此在某些 VM 上,这实际上存储了目标内存地址的 8 分之一,或者它是相对于某物的内存地址,或者它只是稍后查找的键。这取决于 VM 和 GC 实现,了解这些东西的理由为零,你无法从 java 代码中观察到任何这些。

【讨论】:

当我写下我的问题时,我突然意识到原语必须存储在堆栈上(而对象存储在堆上)。如果原语在堆栈上,那么它是如何工作的? int a = 5;诠释 b = 10; System.out.println(a); b 将位于堆栈的顶部,因此要访问 a,Java 是否将 a 洗牌到堆栈的顶部以访问它? java 是按值传递的。 a 被解析,它解析为的 value 被压入堆栈。副本,有效。没有洗牌 - 复制完成。它与System.out.println(a + b) 没有什么不同。如果你觉得这些东西有趣,就开始运行javap,而不是问 SO 问题(它对你作为 Java 程序员的技能的影响为零,所以如果这是你的目标,那就找点别的东西去痴迷吧。但如果你觉得这些东西很有趣,对你有好处!javap -c(即查看字节码)是了解更多信息的下一步。 @chappie – 变量都在堆栈中。基元和对象引用都是一样的,只要它们不是对象的一部分(尽管它们通常被称为 attributesproperties 而不是 variables)。更准确地说,这些变量的值在堆栈上,这些值是……原语或引用! 还有一个问题。每个对象(在堆上)的内存是否像数组一样连续?即汽车类有品牌、型号、价格。品牌、型号和价格是连续内存吗? 是的,单个对象的所有字段数据都是连续的,但是请记住,存储来表示对象的东西只是一个指针。他们指向的实际对象不必是连续的。【参考方案2】:

退后一步……

对象变量存储对对象(的“地址”)的引用。如您所料,这样的对象地址是一个(数字)值。

如果原语的变量是该值的地址,那么对象和原语之间会有什么区别(以及为什么 Java 需要 Integer 类,而它已经有了 ìnt?)

所以,不,原始变量直接保存原始变量的值。

当然,这个值有一个地址,只是这个地址不能被 Java 代码访问。

【讨论】:

"...对象和原语有什么区别" 这就是为什么我问这个问题 b/c 想到了同样的问题。

以上是关于Java原始类型变量存储的主要内容,如果未能解决你的问题,请参考以下文章

Java var定义基本数据类型变量实际类型问题(原始类型或包装类)

JavaScript中基本数据类型和引用数据类型的区别

Java中原始数据类型存放位置理解

什么是int?int和Integer有什么区别?

原始值和引用值

java Type是一个啥接口,啥叫参数化类型,原始类型,类型变量