当前位置: 首页 > 知识库问答 >
问题:

字符串创建和字符数组内存分配

訾渝
2023-03-14

我读过很多关于创建字符串时内存分配的相互矛盾的文章。一些文章说new operator在堆中创建一个字符串,String literal在String Pool[heap]中创建,而一些文章说new operator在堆中创建一个对象,在String Pool中创建另一个对象。

为了分析这一点,我写了下面的程序,打印字符串字符数组和字符串对象的hashcode:

import java.lang.reflect.Field;

public class StringAnalysis {

    private int showInternalCharArrayHashCode(String s)
            throws SecurityException, NoSuchFieldException,
            IllegalArgumentException, IllegalAccessException {
        final Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        return value.get(s).hashCode();
    }

    public void printStringAnalysis(String s) throws SecurityException,
            IllegalArgumentException, NoSuchFieldException,
            IllegalAccessException {
        System.out.println(showInternalCharArrayHashCode(s));

        System.out.println(System.identityHashCode(s));

    }

    public static void main(String args[]) throws SecurityException,
            IllegalArgumentException, NoSuchFieldException,
            IllegalAccessException, InterruptedException {
        StringAnalysis sa = new StringAnalysis();
        String s1 = new String("myTestString");
        String s2 = new String("myTestString");
        String s3 = s1.intern();
        String s4 = "myTestString";

        System.out.println("Analyse s1");
        sa.printStringAnalysis(s1);

        System.out.println("Analyse s2");
        sa.printStringAnalysis(s2);

        System.out.println("Analyse s3");
        sa.printStringAnalysis(s3);

        System.out.println("Analyse s4");
        sa.printStringAnalysis(s4);

    }

}

此程序打印以下输出:

Analyse s1
1569228633
778966024
Analyse s2
1569228633
1021653256
Analyse s3
1569228633
1794515827
Analyse s4
1569228633
1794515827

从这个输出中,有一点非常清楚,不管字符串是如何创建的,如果字符串具有相同的值,那么它们共享相同的字符数组。

现在我的问题是,这个字符存储在哪里,它是存储在堆中还是存储到permgen?我还想了解如何区分堆内存地址和永久内存地址。

如果char数组不存储在permgen中,而是存储在堆中,那么它是否意味着字符串文字也使用堆空间[这是我从未读过的东西]。

共有3个答案

萧繁
2023-03-14

最后一点:根据定义,字面意义的myTestString是内嵌的,所有具有相同值的内嵌String引用都引用相同的物理String对象。因此,字面意义将是intern中的EXACT SAME STRING。

[更正]根据定义,具有相同字符序列值的两个字符串的hashCode(但不是标识HashCode)将是相同的。

另一方面,char[]数组的hashCode只是其地址位的一个乱码,与数组的内容无关。这表明数组在上述所有情况下都是完全相同的数组。

(进一步信息:字符串的旧实现包括指向char[]的指针、偏移量、长度和hashCode值。较新的实现不支持偏移量值,字符串值从数组的元素0开始。其他(非Sun/非Oracle)实现取消了单独的char[]数组和在主堆分配中包含字符串字节。不要求字段实际存在。)

[续]复制了测试用例并添加了几行。hashCode和identityHashCode在给定的char[]上生成相同的值,但在具有相同内容的不同数组上生成不同的值。

事实上,s1和s2中的数组是相同的,这几乎可以肯定是因为它们共享中间文本“myTestString”的char[]数组。如果字符串与“新鲜”的char[]数组分开构造,它们将是不同的。

所有这一切的主要好处是,字符串文本被插入,当使用new String(String)复制字符串时,正在测试的实现“借用”了源代码的数组。

Char array hash codes
a1.hashCode() = 675303090
a2.hashCode() = 367959235
a1 identityHashCode = 675303090
a2 identityHashCode = 367959235
Strings from char arrays
a1 String = ABCDE
a1 String's hash = 62061635
a1 String value's identityHashCode = 510044439
a2 String = ABCDE
a2 String's hash = 62061635
a2 String value's identityHashCode = 1709651096
邹海超
2023-03-14

从这个输出中,有一点非常清楚,不管字符串是如何创建的,如果字符串具有相同的值,那么它们共享相同的字符数组

不完全是这样:这是因为您从一个文本字符串开始,然后从中创建多个实例。在OpenJDK(Sun/Oracle)实现中,如果备份数组代表整个字符串,则会复制它。您可以在src中看到这一点。jar,或者在这里:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/String.java#String. (java.lang.String)

如果仔细构造源字符串,使它们从不同的字符数组开始,就会发现它们不共享支持数组。

现在我的问题是这个字符阵列存放在哪里

据我所知,字符串文本的字符数组存储在堆中(那些对类加载内部元素有更好了解的人,请随意评论)。从文件加载的字符串将始终将其后备数组存储在堆中。

我确定的是intern()使用的数据结构只引用String对象,而不是其字符数组。

蒙经纶
2023-03-14

来自字符串src

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

很明显,使用此构造函数创建的字符串与原始字符串共享字符数组(值)。

需要注意的是,API并不保证这种共享:

初始化新创建的字符串对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要original的显式副本,否则不需要使用此构造函数,因为字符串是不可变的

例如,字符串。子字符串用于与原始字符串共享字符数组,但在最新版本的Java 1.7字符串中。子字符串生成字符数组的副本。

 类似资料:
  • 在这个问题之前,我先要说明一个事实,那就是我学习编程才一个月,而这个学校的作业却把我难住了。具体地说,它是摩尔斯电码到英语翻译器(反之亦然)...这是我被困住的部分:

  • 问题内容: 我在PostgreSQL中有一个表,其中包含: 是类型,它包含具有正确顺序的该行的父记录列表。 家长:然后再 如何编写一个查询,该查询对于任何给定的ID都会生成其父母姓名的字符串? 例如: :。 :。 :。 编辑: 如果可能的话,我希望请求的ID总是会出现。 :。 :。 :。 :。 问题答案: 您可以结合使用诸如generate_subscripts和array之类的多项操作来获得结果

  • 问题内容: 我正在尝试找到一种将String拆分为String数组的方法,并且每当遇到白色香料时就需要对其进行拆分,例如 “嗨,我是保罗” 进入” “嗨”“我”“保罗” 如何使用RegularExpression在split()方法中表示空格? 问题答案: 您需要一个正则表达式,例如,这意味着: 每当遇到至少一个空格时就进行拆分 。完整的Java代码是:

  • 当使用关键字 new 创建字符串时,它使用采用 String 文本的构造函数创建新的 String 对象。我想知道在调用 String 构造函数之前,文本是否存储在常量池中。 我这么问的原因是,在《OCA Java SE 7程序员I认证指南》中,Mala Gupta写道: 她在第一行表示,new创建的String对象不会存储在常量池中。这很好,但不清楚的是,第一行构造函数中的字面“Summer”是

  • 字符串 字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似: 单双引号的区别: 双引号里可以有变量,单引号则原样输出; 双引号里可以出现转义字符,单引号则原样输出; 单引号字串中不能出现单引号。 拼接字符串 #!/bin/bash str1='i' str2='love' str3

  • 这些内建函数作用于表达式左侧的字符串值。 如果左侧值是数字或日期/时间/日期-时间或布尔类型(从 2.3.20 版本开始), 根据当前的number-, date/time/date-time- 和 boolean-format设置, 那么它会自动被转成字符串值(当使用${...} 插入这些值时,应用的都是一样的格式程序)。 boolean 字符串转为布尔值。字符串必须是 true 或 false