繁体   English   中英

Java字符串不变性和使用相同的字符串值创建新的字符串

[英]Java String Immutability and Using same string value to create a new string

我知道问题的标题不是很清楚,对此感到抱歉,不知道如何提出。 我有一个非常基本的Java实现问题,我想着重于应用程序性能,但是它也涉及Java中的String创建模式。

我了解Java中Strings的不变性概念。 我不确定的是,我在某处读到以下内容不会创建两个不同的String对象:

String name = "Sambhav";
String myName= "Sambhav";

我想知道Java是怎么做到的? 它实际上是否在程序存储器中寻找一个String值并检查其是否存在,如果不存在则创建一个新的String对象? 在那种情况下,显然可以节省内存,但存在性能问题。

还可以说我有这样的代码:

  public void some_method(){
        String name = "Sambhav";
        System.out.println(name); //  or any random stufff
  }

现在,在每次调用此函数时,是否正在创建一个新的String并将其添加到内存中,或者我是否使用相同的String对象? 我只是想知道所有事情的真相?

如果我们这样说

String name = "Sambhav";
String myName= "Sambhav";

不会因为引用而创建新对象,那

String name = new String("Sambhav");
String myName= new String("Sambhav");

Java是否仍然可以捕捉到该字符串相同,只是将myName指向与上一条语句中创建的对象相同的对象?

字符串是内部char数组,具有一些与基础char数组一起使用的固有功能。 例如。 subString(int),split(String)方法。

字符串是不可变的,这意味着更改字符串引用所做的任何努力都会创建一个新的字符串并为此分配内存。 如下

line 1. String a = new String("SomeString");
line 2. a = "SomeStringChanged";

第1行使用变量a引用的“ SomeString”分配内存,并将“ SomeString ”添加到字符串池

第2行在字符串池中使用“ SomeStringChanged”分配内存,并且被aie a引用,现在不指向“ SomeString”,并且“ SomeString”占用的内存现在可用于gc

这里没有重复使用

line 3. String  b =  "SomeStringChanged";

现在,文字“ SomeStringChanged ”被变量ab 重用 也就是说,它们指的是相同的内存位置 ,实际上是指称为“ 字符串池 ”的位置。

line 4. a = new String("SomeStringChanged");

现在,新的分配完成包含“SomeStringChanged”,通过引用

现在没有重用发生。 (字符数组SomeStringChanged的字符串池中已经存在。因此,没有字符串池分配发生)

line 5. a = new String("SomeStringChanged").intern();

现在,在第4行中创建的分配将被丢弃,变量ab指向包含“ SomeStringChanged”的字符串池中的相同位置。 这里重复使用相同的char数组。 功劳归于intern()方法

line 6. String x = new String("SomeX");
line 7. String y = "SomeX";

第6 行将在堆和字符串池中为SomeX创建一个分配。 char数组重复。

第7行不会为SomeX分配任何内存,因为它已经在字符串池中了

Line 8 String s = new String(someStringVariable);

第8行将仅在堆中分配单个内存位置,而不在字符串池中分配单个内存位置。

总之,只有在将String引用声明为文字或将String对象声明为intern的情况下,才可以重用字符串的char数组,即只有这两个可以使用String池(这实际上是char数组重用的思想)。

在源文件中“像这样”在引号中加上的字符串是编译时常量 ,如果它们的内容匹配,则它们由类的字节码表示形式内的常量池中的单个条目表示,因此表示单个String对象在运行时。

String name = new String("Sambhav");
String myName= new String("Sambhav");

它们显然是不同的对象,尽管可以重用基础字符串的char数组(您在构造函数中提供的字符数组),但每次调用都会创建一个新的String对象。 发生这种情况的原因是通过关键字设想了Java创建新对象。 这就是为什么在这种情况下,即使name.equals(myName)名称为== myName的原因

String name = new String("Sambhav");

String myName = new String("Sambhav");

Java是否仍然可以捕捉到该字符串相同,只是将myName指向与上一条语句中创建的对象相同的对象?

JVM通过计算hash设法仅保留一个相等的String对象的引用。

这些String对象保存在String pool

字符串池

串池(有时也被称为串canonicalisation)是更换几个的处理String与相等的值,但具有单个共享不同标识对象String对象。

您可以通过保留自己的Map<String, String> (根据需要使用软引用或弱引用)并将映射值用作规范化值来实现此目标。

或者,您可以使用JDK提供给您的String.intern()方法。

JVM版本之间的快速字符串池差异

在Java 6中,此String pool位于Perma Gen内存中。 该内存通常很小且有限。 另外,这里不应该使用String.intern()因为您可能会耗尽内存。

在Java 7和8中,将其取出到heap内存中并使用像数据结构这样的hash-table实现。

由于类似hash-table结构( HashMapWeakHashMap )以恒定的复杂度使用计算的hash来访问条目,因此整个过程非常快。

正如提到的这个文章:

  • 由于Java字符串池存储使用固定大小的内存区域(PermGen),因此请远离Java 6上的String.intern()方法。

  • Java 7和8在堆内存中实现了字符串池。 这意味着您受Java 7和8中用于字符串池的整个应用程序内存的限制。

  • 在Java 7和8中使用-XX:StringTableSize JVM参数来设置字符串池映射大小。 它是固定的,因为它被实现为在存储桶中具有列表的哈希映射。 估计应用程序中的不同字符串的数量(打算插入),并将池大小设置为等于该值附近的一些质数。 它将允许String.intern()在恒定时间内运行,并且每个插入的字符串都需要相当小的内存消耗(对于相同的任务,显式使用的Java WeakHashMap将消耗4-5倍的内存)。

  • -XX:StringTableSize参数的默认值在Java 7中为1009 ,在Java 8中为25-50K左右。

实际上,您在显示3种不同的原因,这些字符串可能会在内部使用相同的缓冲区。 请注意,共享缓冲区仅可用于单独的实例,因为它们是不可变的。 否则,缓冲区中的更改也将反映在其他变量值中。

  1. 编译器检测相同的String文字。 如果重复字符串文字,则编译器可以简单地指向同一对象实例;

  2. 对String的引用指向相同的对象实例,因此在定义上相同。

  3. 缓冲区共享可能在使用new构建过程中有所帮助。 如果运行时系统发现可以共享String内容,则可以选择共享; 但是, 不能保证此行为-具体取决于实现。 对象实例应该是不同的(但是将它们用作单独的实例仍然不明智)。

作为#3的示例,Java 6 OpenJDK源代码只会指向相同的缓冲区。 如果缓冲区大于新的String实例,则将创建一个副本。 这些是明确不同的对象,尽管可以重用基础字符串的char数组(您在构造函数中提供的字符串),但每次调用都会创建一个新的String对象,以便垃圾回收器可以清除较大的字符串(否则较大的字符缓冲区)可能会无限期地保存在内存中)。

除非您变得粗心并开始使用==进行相等(或其他将==equals混淆的构造),否则所有这些对您来说都不重要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM