一个对象在堆中是如何存储的?

20

一个对象在堆中是如何被存储的。例如,自行车类可以这样定义:

public class Bicycle {

   public int gear;
   public int speed;

   public Bicycle(int startSpeed, int startGear) {
       gear = startGear;
       speed = startSpeed;
   }

   public void setGear(int newValue) {
       gear = newValue;
   } 

   public void applyBrake(int decrement) {
       speed -= decrement;
   }

   public void speedUp(int increment) {
      speed += increment;
   }   
}

那么我可以创建一个自行车对象:

Bicycle bicycle = new Bicycle(20,10)

那么这个自行车对象应该存储在堆中。但我不理解堆是如何精确地存储这些实例变量和方法,比如速度和齿轮。我了解堆应该被实现为树。那么对象是如何存储在树中的呢?当你使用 bicycle.speed 来查找速度值时,时间复杂度会是什么?


可能是堆栈和堆在哪里?的重复问题。 - EkoostikMartin
@fge 我了解堆应该被实现为一棵树。那么对象是如何存储在树中的? - Jude
2
堆只是一个巨大的字节数组。对象是一个结构——一系列字段,被视为单个实体并一起分配。要创建一个对象,您需要找到一个合适大小的“空闲”堆位,设置对象头(指向其类和其他“簿记”信息的指针),并执行类中描述的初始化。 - Hot Licks
3个回答

36

想让别人帮你完成计算机科学作业?;)

根据语言的不同,具体实现将有所不同(如何存储在内存中),但是一般概念相同。

您有自己的堆栈内存和堆内存,局部变量和参数放在堆栈上,每当您new一些东西时,它就会进入堆。它被称为堆栈,因为值在声明或调用函数时被推入它,并弹出并超出范围。

                    +--------------+
|              |    |              |
|              |    |              |
|              |    |              |
|              |    |              |
|              |    |              |
|              |    |              |
+--------------+    
     Stack                Heap

每个实例变量占用与其类型相同大小的内存(取决于语言), 编译器将所有这些加起来,这就是类型的sizeof(如C ++)。方法进入代码空间,不会new进入对象(我认为现在您最好不要考虑此事而学习如何组织内存,只需将其视为魔术即可)。

因此,在您的示例中:

Bicycle bicycle = new Bicycle(20,10)
  • bicycle是指向堆内存地址的引用,在大多数语言/系统中,它会占用32位或64位的栈空间。
  • new在堆中分配内存。编译器计算出Bicycle的大小,并创建相应的汇编/机器代码来分配所需的内存量。

这是该行代码执行后内存的情况:

                    +--------------+
|              |    | Bicycle obj  |
|              |    |--------------|
|              |    |              |
|              |    |              |
|--------------|    |              |
| bicycle ref  |    |              |
+--------------+    
     栈                        堆

更具体地说,由于Bicycle类有两个实例变量(或Java中称为字段),都是int类型,而Java中的int为32位或4字节,因此您的自行车对象的大小为4个字节*2个字段=8个字节。

                   +-------------+
|             |   0| gear        | 
|             |   4| speed       |
|             |    |-------------|
|             |   8|             |
|-------------|  12|             |
| bicycle=0x4 |    |             |
+--------------+    
     栈                        堆

访问内存的时间复杂度为O(1)。编译器能够找到speed的确切内存地址,因为作为对象的第二个int字段,它在bicycle+0x4处。


2
谢谢你的解释,威尔!我因为一个也被称为“堆”的数据结构而感到困惑。这个链接也帮了我很多。我有一个问题想问你:成员函数存储在哪里?它们也存储在堆中吗? - Jude
成员函数存储在堆栈中 @Jude - Kick
1
是的,堆ADT完全不同。我认为在你理解它之前,你需要在更低层次的计算机架构方面建立更多的知识。
  1. 实际代码(字节码或汇编)存储在其他地方的代码空间中。
  2. 参数值被推入堆栈(与一些回调/返回内存地址一起),然后移动当前行指针。
  3. 堆中的对象保持不变,直到它们被销毁。如上图所示,引用实际上只是一个内存地址的整数。
- Will Chen
非常完整的答案。 - dreamcrash

3
Bicycle bicycle = new Bicycle(20,10)

参考自行车将存储在堆栈中,而对象和实例变量将存储在中,并且堆的地址被分配在堆栈中,因此可以说堆栈将链接到堆。


2
我知道引用会在堆栈中,但我不确定对象在堆中是如何存储的。 - Jude

2

首先,您应该理解 Java 中“对象”的含义。

对象实际上只是 Heap 中的缓冲区(内存区域)。那个缓冲区或内存区域称为对象。

对象包含类的所有非静态数据成员。

所有的-

对象存储在 Heap 中。

静态数据成员存储在 Class Area 中。

引用变量存储在 Stack 中。

方法(静态或非静态)存储在 Method Area 中。

Memory Area

阅读更多关于 Java 内存模型的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接