在Java中,字符串(以及通常的其他类型/对象)是如何在内存中存储和组织的



我想知道Java是如何将String对象存储在内存中的,让我们以以下代码为例:

String s1 = "Hello";
String s2 = new String("Hello");

如果我理解正确的话,s1引用了";你好";,而CCD_ 2引用存储器的新部分;你好";,因此,如果我们想象内存被划分为常量静态堆栈中的s1s2;你好"在常量和新创建的包含";你好"在中。我说得对吗?

如果我们只分析这段代码:

String s = new String("Hello");

当我们启动程序时,我希望它能做的是;你好"字符串,然后它复制此常量字符串并将其放入,然后将此堆栈片段的地址提供给中包含的s。这是正确的吗?或者它可能放在中,而不是字符串的副本。但实际地址呢?但这不意味着,如果我们回到s1s2s1s2的代码,以某种方式,引用相同的内存地址,唯一的区别是s1->quot;你好"并且CCD_ 11->->quot;你好"?我知道,如果我们谈论Java,我不应该问这些问题,因为JVM制造了这些东西,我不该关心它,但这是我个人的知识。

JVMS给出了一个简单的形式定义,§2.5.3

堆是运行时数据区,从中为所有类实例和数组分配内存。

因此,根据定义,字符串对象存储在堆内存中。

其他一切都是实现细节,JVM实现之间甚至不同版本之间可能会有所不同。

通常,String对象与其封装的字符内容不同。字符通常存储在数组中。因此,当您通过new String("Hello")创建新对象时,两个String实例仍然可以在幕后共享数组*。有些实现甚至可以搜索相等的字符串,并修改它们以共享阵列(如果还没有(,以节省内存,请参阅Java 8 的字符串重复数据消除功能

即使对于常量字符串,数组通常也是与常量数据不同的普通堆对象。类文件中的字符以"修改的UTF-8"格式存储,这不适用于基于UTF-16单元提供随机访问的字符串API。

因此,第一次为"Hello"常量创建String对象时,字符数据将被转换为数组。在最近基于OpenJDK的实现中,它将是一个byte[]数组,使用ISO-LATIN-1或UTF-16作为内容,然后由String实例封装。在JDK9之前,它是一个char[]数组。

对同一常量的后续访问将使用常量数据来查找已存在的对象。通常,所有代码位置在第一次执行后都会永久链接到对象,因此在随后的执行中不必再次查找。

但是,当您使用类数据共享时,存档可能已经包含预构建的字符串对象,这些对象不需要从类文件数据转换。如前所述,这都是针对具体实施的。

*在参考实现中,s20构造函数的行为随着Java 7更新6而改变。从那时起,它将共享阵列。

最新更新