Memory Allocation of Strings in Java
在Java中,字符串作为String类的对象存储在内存中。
当为任何Java程序分配内存时,JVM(Java虚拟机)将分配的内存分为两部分。一部分是栈,另一部分是堆。在堆内存中,java分配了一些内存,特别是对于字面量,这块内存被称为字符串常量池(SCP)。 SCP 是堆内预定义的区域。字符串池有助于为 Java 运行时节省大量空间。 String 类使用 SCP 来存储唯一的字符串文字。
在堆栈内存中,存储变量或变量引用或对象的引用。
堆内存中存储了所有动态分配的对象。为了给对象分配内存,我们使用 new 关键字。
创建字符串对象有两种方法。
- 字符串文字
字符串 str1 = “MyString”;
每当我们创建一个字符串文字时,JVM 首先检查该字符串文字是否已存在于字符串常量池中。如果不可用,它将在 SCP 中创建一个新的字符串文字。
上图中,str1指向SCP中的“MyString”。以下是处理新创建的字符串文字的方式。
- 使用新关键字
String str2 = new String(“MyString”); //使用new关键字实例化字符串类
当使用 new 关键字创建字符串对象时,它将创建两个对象。一个在 SCP 中,另一个在堆中,引用变量存储在堆栈中。
我们已经使用
创建了文字“MyString”字符串 str1 = “MyString”;
由于 SCP 中不能有重复项,因此 JVM 不会在 SCP 中再创建一个对象,而是返回对堆栈中变量 str3 的现有引用,并在堆中创建一个对象。 Str3 将指向堆中的对象“MyString”,但不指向 SCP 中的对象。
以下是如何为字符串对象分配内存的不同情况。
情况1:上面定义的字符串对象如何存储在内存中。
公共类字符串存储概念
{
public static void main(String[] args)
{
字符串 str1 = “MyString”;
String str2 = new String(“MyString”);
System.out.println(str1 == str2); //输出:False
System.out.println(str1.equals(str2)); //输出:True
}
}
当我们使用“==”运算符比较 str1 和 str2 时,它返回 false。众所周知,“==”运算符比较它们的物理地址。在我们的示例中,str1 指向 SCP 中的对象,str2 指向堆中的对象。所以它返回 false。
但在 str1.equals(str2) 的情况下,正如我们所知,“equals”函数会检查 str1 和 str3 中的各个字符是否具有相同的存储值,并返回 true。
情况 2:另一个字符串文字
字符串 str3 = “MyString”;
str1 和 str3 都将指向 SCP 中的同一个字符串。
公共类字符串存储概念
{
public static void main(String[] args)
{
字符串 str1 = “MyString”;
字符串 str3 = “MyString”;
System.out.println(str1 == str2); //输出:True
System.out.println(str1.equals(str2)); //输出:True
}
}
s1 == s3 返回 true,因为“==”运算符比较它们的物理地址,但不比较内容。
s1.equals(s3) 返回 true,“equals” 函数检查两个引用变量中的单个字符。
情况 3:使用新关键字创建另一个字符串对象
String str4 = new String(“NewString”);
在这种情况下,JVM会在SCP中检查该字符串,但找不到值为“NewString”的字符串对象,因此会创建两个对象,一个在SCP中,另一个在Heap中,引用变量str4将存储在堆栈。 Str4 将拥有对堆中对象的引用。
情况 4:创建另一个字符串文字。
字符串 str5 = “NewString”;
在这种情况下,JVM 将在 SCP 中检查该文字是否已经可用,这里“NewString”已经存在于 SCP 中,因此 JVM 不会在 SCP 中创建重复项,而是返回对变量 str5 的引用。
情况 5:将一个字符串分配给另一个字符串
String str4 = new String(“NewString”);
字符串 str6 = str4; //赋值
这里str6和str4会指向Heap中的同一个对象,不会擦除str4中的值。
公共类字符串存储概念
{
public static void main(String[] args)
{
String str4 = new String(“NewString”);
字符串 str6 = str4;
System.out.println(str4 == str6); //输出:true
}
}
JVM会将堆中“NewString”的引用赋予变量str6。这就是 str4 == str6 返回 true 的原因。
总而言之,使用字符串文字和“new”运算符创建字符串对象有其优点和缺点。
通过使用字符串文字,我们可以通过不创建重复项来提高内存效率。 JVM 创建一个唯一的对象,并且该字符串永远保留在 SCP 中。这样做的缺点是字符串池的大小是固定的,有时会变满。
但通过使用 new 关键字,它创建了两个对象,一个在 SCP 中,另一个在堆中。在堆中,如果我们不需要该对象,垃圾收集器将删除该对象以腾出空间。但这样做的缺点是,使用“new”运算符,JVM 总是必须创建一个新对象,这对 JVM 来说是一个重载。