【问题标题】:How is the zero length array represented in memory?零长度数组如何在内存中表示?
【发布时间】:2015-07-19 21:43:28
【问题描述】:

Java 原语对象被映射到本机原语。
所以我的问题是char value[] = new char[0]; 是如何表示的?
它是否取决于(本机代码的)gcc 编译器实现?这是否意味着所有空的 Java Strings 都指向同一个地址?

【问题讨论】:

  • Java 原语不一定映射到本机原语。 Java int 是 4 个字节,即使它是在 8 位架构上实现的。 Java char 总是两个字节。
  • 您是指String 对象本身还是字符串的字符内部表示?
  • 一个空字符串将是一个长度为0 的对象,并且该length 应该存储在某个地方。是否所有空字符串都指向同一个地址有关系吗?
  • @CaptainMan:内部表示
  • @Jim,空数组和空数组是完全不同的,你不能将一个交换另一个。

标签: java arrays string char native-code


【解决方案1】:

Java 数组是对象。它们继承自 Object 类。

JVM 规范没有规定对象的任何特定实现,只要它们的行为符合规范即可。在实践中,它是通过一个标头后跟对象的实际字段来实现的。

Java 中的数组只是其原始组件的序列。它是一个对象,具有length 字段,并且具有方法。因此,与任何其他对象一样,它具有标头,然后是长度,然后是所有数组组件。

分配大小为零的数组是具有标头和大小但没有为实际组件分配空间的对象。

对数组的引用就像对任何其他对象的引用一样。 Java 中的数组与 C 中的数组不同,如果数组大小为零,则指向其开头的指针实际上是无效的。对数组的引用指向数组object,它的长度恰好为零并且没有实际项目。如果您尝试寻址此类数组中的任何元素,则不会出现有效指针问题。数组引用本身指向一个有效对象。然后,边界检查将显示任何索引超出范围,因此不会发生进一步的指针取消引用。

所以底线是对char[0] 的引用是对实际分配对象的有效引用。它根本没有超出长度的数据。

这与null 不同,null 是一个位全为零的引用,因此根本不指向任何地方。除了引用本身之外,没有分配任何内存,而对于char[0],为标头和长度分配了足够的内存。


对于字符串,两个空字符串不一定指向同一个字符数组。例如,如果你写:

String a = new String();
String b = new String();

你会得到两个不同的空字符串对象。它们中的每一个都有一个它指向的不同的空字符数组。这是因为String 类的无参数构造函数是这样实现的:

public String() {
    this.value = new char[0];
}

您看到new 关键字的用法了吗?这意味着分配了一个新的数组对象,而不是从任何地方复制。

但请注意,如果您的来源是:

String a = "";
String b = "";

然后由于实习,它们将指向同一个字符串对象,因此指向同一个字符数组。另外,如果是:

String a = new String();
String b = new String(a);

然后你会有两个不同的String 对象,但它们都指向同一个内部字符数组。这是因为第二行的构造函数是:

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

同样,指向空字符串的指针肯定不同于空指针。它指向一个实际的字符串对象,该对象指向一个实际的字符数组对象。

【讨论】:

【解决方案2】:

内存布局未定义,因为它是一个实现细节。

这是他们的 64 位 JVM 的 how IBM describes the memory layout of an array

  1. 64 位类指针(即信号 char
  2. 64 位用于标志(例如,表示此对象是一个数组)
  3. 64 位用于锁定数据(用于同步)
  4. 64 位数组长度(仅使用 32 位,但字段边界对齐)
  5. 0 位数据,因为数组没有元素

总共 256 位或 32 字节。

在 Java 中,Stringchar[] 不是一回事。 String 将是一个单独的对象,其中包含对 char[] 的引用。

【讨论】:

  • char[0]的内存布局是什么?
  • @Jim 答案试图以位级细节来解释这一点。有什么不清楚的地方吗?
  • 所以基本上char[0]在访问时会转到第4个字并找到长度为0,这意味着不存在数据,即第5个字及以上没有有效的内存地址。那有什么帮助,而不是仅仅有 null 呢?而且在 C++ 中也不一样吧?
  • null 不是一个对象。 null 与空数组完全不同。
  • @Jim 我不明白。您的意思是“为什么要使用空数组而不是 null?”有很多原因,在别处详细描述,但没有一个是关于内部内存表示的。
【解决方案3】:

由于每个数组对象都有length属性,所以写的时候

char a[] = new char[0];

然后length 属性得到值 0,它代表数组的大小。 length 字段为 4 个字节,该数组有一个通常为 8 个字节的正常标头。

空数组没有什么特别之处,它和其他数组一样,但它不包含元素。

值得一提的是,空数组和初始化为null 的数组是两个不同的东西。例如,有时从方法返回空数组比 null 更容易。

【讨论】:

    【解决方案4】:

    使用new 创建的两个不同对象在引用相等性方面必须不同,所以不,它们不是同一个对象。

    另外,任何两个 Java String 对常量字符串 "" 的引用都将引用同一个对象,因为编译时常量字符串会被保留。

    【讨论】:

    • 那么char[0] 是一个有效的内存地址吗?因为我认为这是编译器的某种“语法”糖
    • 不,new char[0] 创建一个新的、完全有效的空数组对象。
    • 空数组对象是什么意思?这是一个长度为 0 的数组。这个“完全有效的空数组对象”的表示是什么?
    • 是的,它是一个长度为 0 的数组。在 32 位 HotSpot 上,数组由所有对象的正常 8 字节标头表示,长度字段用 4 字节表示,然后是数组的内容,零长度数组的工作方式与任何其他数组相同。
    • 一贯如此。它被四舍五入到填充大小的倍数。 零长度数组没有什么特别之处。
    猜你喜欢
    • 1970-01-01
    • 2012-09-06
    • 1970-01-01
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    相关资源
    最近更新 更多