gpt4 book ai didi

java - 数组对象是否明确包含索引?

转载 作者:行者123 更新时间:2023-12-03 00:09:44 28 4
gpt4 key购买 nike

从学习 Java 的第一天起,各种网站和许多老师就告诉我,数组是连续的内存位置,可以存储指定数量的所有相同类型的数据。

由于数组是一个对象,对象引用存储在堆栈中,而实际对象存在于堆中,因此对象引用指向实际对象。

但是当我看到如何在内存中创建数组的示例时,它们总是显示如下内容:

(其中对数组对象的引用存储在堆栈中,该引用指向堆中的实际对象,其中还有指向特定内存位置的显式索引)

Internal working of objects taught by various websites and teachers

但是最近我遇到了online notes在 Java 中,他们声明数组的显式索引未在内存中指定。编译器只知道在运行时查看提供的数组索引号去哪里。

像这样:

Enter image description here

看完笔记,我也在谷歌上搜索了这件事,但是这个问题的内容不是很模糊就是没有。

关于这个问题,我需要更多的澄清。数组对象索引是否明确显示在内存中?如果不是,那么 Java 如何在运行时管理命令以转到数组中的特定位置?

最佳答案

Does an array object explicitly contain the indexes?



简答:不。

更长的答案:通常不会,但理论上可以。

完整答案:

Java 语言规范和 Java 虚拟机规范都没有保证数组是如何在内部实现的。它所需要的只是数组元素由 int 访问。索引号的值来自 0length-1 .实现如何实际获取或存储这些索引元素的值是实现私有(private)的细节。

一个完全一致的 JVM 可以使用 hash table来实现数组。在这种情况下,元素将是不连续的,分散在内存中,并且需要记录元素的索引,以了解它们是什么。或者它可以向月球上的人发送消息,他将数组值写在贴有标签的纸片上,并将它们存储在许多小文件柜中。我不明白为什么 JVM 会想要做这些事情,但它可以。

在实践中会发生什么?典型的 JVM 会将数组元素的存储分配为平坦、连续的内存块。定位特定元素很简单:将每个元素的固定内存大小乘以所需元素的索引并将其添加到数组开头的内存地址: (index * elementSize) + startOfArray .这意味着数组存储只包含原始元素值,按索引顺序连续排列。没有必要将索引值与每个元素一起存储,因为元素在内存中的地址暗示了它的索引,反之亦然。但是,我不认为您展示的图表试图说明它明确存储了索引。图表只是在图表上标记元素,以便您知道它们是什么。

使用连续存储并通过公式计算元素地址的技术简单且非常快速。它也有很少的内存开销,假设程序只分配他们真正需要的数组。程序依赖并期望数组的特定性能特征,因此对数组存储做了一些奇怪的事情的 JVM 可能会表现不佳并且不受欢迎。因此,实际的 JVM 将被限制为实现连续存储,或执行类似的操作。

我只能想到该方案的几个变体,它们将永远有用:
  • 堆栈分配或寄存器分配的数组:在优化期间,JVM 可能会通过 escape analysis 确定一个数组只在一个方法中使用,如果数组也是一个较小的固定大小,那么它将是一个理想的候选对象,可以直接在堆栈上分配,计算相对于堆栈指针的元素地址。如果数组非常小(固定大小可能最多 4 个元素),JVM 可以更进一步,将元素直接存储在 CPU 寄存器中,所有元素访问都展开和硬编码。
  • 打包 boolean 数组:计算机上最小的可直接寻址的内存单元通常是一个 8 位字节。这意味着如果 JVM 为每个 boolean 元素使用一个字节,那么 boolean 数组每 8 位就会浪费 7 位。如果 boolean 值在内存中打包在一起,则每个元素仅使用 1 位。这种打包通常不会完成,因为提取单个字节的速度较慢,并且需要特别考虑多线程的安全性。但是,打包 boolean 数组在某些内存受限的嵌入式设备中可能非常有意义。

  • 尽管如此,这些变体都不需要每个元素存储自己的索引。

    我想谈谈你提到的其他一些细节:

    arrays store the specified number of data all of the same type



    正确的。

    数组的所有元素都相同的事实 类型 很重要,因为这意味着所有元素都相同 尺寸 在内存中。这就是通过简单地乘以它们的共同大小来定位元素的原因。

    如果数组元素类型是引用类型,这在技术上仍然是正确的。尽管在这种情况下,每个元素的值不是对象本身(可能具有不同的大小),而只是引用对象的地址。此外,在这种情况下,数组的每个元素引用的对象的实际运行时类型可以是元素类型的任何子类。例如。,
    Object[] a = new Object[4]; // array whose element type is Object
    // element 0 is a reference to a String (which is a subclass of Object)
    a[0] = "foo";

    // element 1 is a reference to a Double (which is a subclass of Object)
    a[1] = 123.45;

    // element 2 is the value null (no object! although null is still assignable to Object type)
    a[2] = null;

    // element 3 is a reference to another array (all arrays classes are subclasses of Object)
    a[3] = new int[] { 2, 3, 5, 7, 11 };

    arrays are consecutive memory locations



    如上所述,这不一定是真的,尽管在实践中几乎肯定是真的。

    更进一步,请注意,尽管 JVM 可能会从操作系统分配一块连续的内存,但这并不意味着它最终在物理 RAM 中是连续的。操作系统可以给程序一个 virtual address space它表现得好像是连续的,但是内存的各个页面分散在不同的地方,包括物理 RAM、磁盘上的交换文件,或者如果已知它们的内容当前为空白,则根据需要重新生成。即使虚拟内存空间的页面驻留在物理 RAM 中,它们也可以以任意顺序排列在物理 RAM 中,使用复杂的页表定义从虚拟地址到物理地址的映射。即使操作系统认为它正在处理“物理 RAM”,它仍然可以在模拟器中运行。可以层层叠叠,这是我的观点,深入到它们的底部 all找出真正发生的事情需要一段时间!

    编程语言规范的部分目的是将明显的行为与实现细节分开。在编程时,您通常可以单独按照规范进行编程,而不必担心它在内部是如何发生的。然而,当您需要处理有限速度和内存的现实世界约束时,实现细节变得相关。

    Since an array is an object and object references are stored on the stack, and actual objects live in the heap, object references point to actual objects



    这是正确的,除了你说的关于堆栈的内容。对象引用可以存储在堆栈中(作为局部变量),但它们也可以存储为静态字段或实例字段,或者如上例所示的数组元素。

    此外,正如我之前提到的,聪明的实现有时可以直接在堆栈上或 CPU 寄存器中分配对象作为优化,尽管这对程序的明显行为的影响为零,只有其性能。

    The compiler just knows where to go by looking at the provided array index number during runtime.



    在 Java 中,执行此操作的不是编译器,而是虚拟机。数组是 a feature of the JVM itself ,因此编译器可以将使用数组的源代码简单地转换为使用数组的字节码。然后由 JVM 来决定如何实现数组,而编译器既不知道也不关心它们是如何工作的。

    关于java - 数组对象是否明确包含索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34368385/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com