其他分享
首页 > 其他分享> > 第十三章 StringTable

第十三章 StringTable

作者:互联网

翻篇是很重要的能力,从悲伤中大方走出来,就是艺术家

1.String的基本特性

String s1 = “baidu”; //字面量的定义方式
String s2 = new String("hello");

String在jdk9中存储结构变更

动机

该类的当前实现String将字符存储在一个 char数组中,每个字符使用两个字节(十六位)。从许多不同应用程序收集的数据表明,字符串是堆使用的主要组成部分,而且大多数String对象仅包含 Latin-1 字符。此类字符仅需要一个字节的存储空间,因此此类对象的内部char数组中的 一半空间未使用。String

描述

我们建议将String类的内部表示从 UTF-16char数组更改为byte数组加上编码标志字段。新String类将根据字符串的内容存储编码为 ISO-8859-1/Latin-1(每个字符一个字节)或 UTF-16(每个字符两个字节)的字符。编码标志将指示使用哪种编码。

与字符串相关的类(例如AbstractStringBuilder、StringBuilder和 )StringBuffer将被更新为使用相同的表示形式,HotSpot VM 的内在字符串操作也是如此。

这纯粹是一个实现更改,对现有的公共接口没有任何更改。没有计划添加任何新的公共 API 或其他接口。

迄今为止完成的原型设计工作证实了内存占用的预期减少、GC 活动的大幅减少以及在某些极端情况下的轻微性能回归。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
   @Stable
   private final byte[] value;
}

结论:String再也不用char[ ] 来存储了,改成了byte[ ] 加上编码标记,节约了一些空间。

String的基本特性

2.String的内存分配

StringTable为什么要调整?

JDK 7中,内部字符串不再分配在Java堆的永久代中,而是分配在Java堆的主要部分(称为年轻代和老年代),与应用程序创建的其他对象一起。这种变化将导致更多的数据驻留在主Java堆中,而更少的数据在永久代中,因此可能需要调整堆的大小。大多数应用程序将看到由于这一变化而导致的堆使用的相对较小的差异,但加载许多类或大量使用String.intern( )方法的大型应用程序将看到更明显的差异。

代码示例
/**
* jdk6中:
* -XX:PermSize=6m -XX:MaxPermSize=6m -Xms6m -Xmx6m
*
* jdk8中:
* -XX:MetaspaceSize=6m -XX:MaxMetaspaceSize=6m -Xms6m -Xmx6m
*/
public class StringTest3 {

   public static void main(String[] args) {
       //使用Set保持着常量池引用,避免full gc回收常量池行为
       Set<String> set = new HashSet<String>();
       //在short可以取值的范围内足以让6MB的PermSize或heap产生OOM了。
       short i = 0;
       while(true){
           set.add(String.valueOf(i++).intern());
      }
  }

}

3.intern( )的使用

当调用intern方法时,如果池子里已经包含了一个与这个String对象相等的字符串,正如equals(Object)方法所确定的,那么池子里的字符串会被返回。否则,这个String对象被添加到池中,并返回这个String对象的引用。
由此可见,对于任何两个字符串s和t,当且仅当s.equals(t)为真时,s.intern( ) == t.intern( )为真。
所有字面字符串和以字符串为值的常量表达式都是interned。
返回一个与此字符串内容相同的字符串,但保证是来自一个唯一的字符串池。
public native String intern();
String myInfo = new string("I love alibaba").intern();
("a"+"b"+"c").intern() == "abc"
/**
* 如何保证变量s指向的是字符串常量池中的数据呢?
* 有两种方式:
* 方式一: String s = "shkstart";//字面量定义的方式
* 方式二: 调用intern()
*         String s = new String("shkstart").intern();
*         String s = new StringBuilder("shkstart").toString().intern();
*/

new String(“ab”)会创建几个对象
/**
* new String("ab") 会创建几个对象?
* 看字节码就知道是2个对象
*/
public class StringNewTest {
   public static void main(String[] args) {
       String str = new String("ab");
  }
}
new String(“a”) + new String(“b”) 会创建几个对象
/**
* new String("a") + new String("b") 会创建几个对象?
*/
public class StringNewTest {
   public static void main(String[] args) {
       String str = new String("a") + new String("b");
  }
}
 0 new #2 <java/lang/StringBuilder> //new StringBuilder()
3 dup
4 invokespecial #3 <java/lang/StringBuilder.<init> : ()V>
7 new #4 <java/lang/String> //new String()
10 dup
11 ldc #5 <a> //常量池中的 “a”
13 invokespecial #6 <java/lang/String.<init> : (Ljava/lang/String;)V> //new String("a")
16 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;> //append()
19 new #4 <java/lang/String> //new String()
22 dup
23 ldc #8 <b> //常量池中的 “b”
25 invokespecial #6 <java/lang/String.<init> : (Ljava/lang/String;)V> //new String("b")
28 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;> //append()
31 invokevirtual #9 <java/lang/StringBuilder.toString : ()Ljava/lang/String;> //toString()里面会new一个String对象
34 astore_1
35 return

我们创建了 6 个对象

@Override
public String toString() {
   // Create a copy, don't share the array
   return new String(value, 0, count);
}
 

标签:String,对象,StringTable,intern,第十三章,字符串,new,常量
来源: https://www.cnblogs.com/l12138h/p/16615196.html