【java基础】-- String

内部实现

String 底层实现是用final修饰的,典型的Immutable类,java9之前内部的实际存储结构是final修饰的char[],java9之后改为了final修饰的byte[],目的也很明显,String几乎是用途最广泛的类型之一,如果能减少他的实际存储大小,那就能大大的缩减内存中的使用。(缩减算法可能类似于霍夫曼编码,还有待考证)

字符拼接

由于String是不可变的,所以其频繁的修改和变动会带来大量的中间对象,StringBuilder和StringBuffer就是为了处理频繁拼接和修改的,原理是维护一个char数组,修改的过程只修改该数组,直到最后toString的时候再生成String对象,这样可以避免很多中间临时对象给gc造成负担。二者的区别也就是前者非线程安全,后者线程安全(也就是在所有修改方法上添加了synchronize关键字而已)

那我们平时的拼接就要尽量避免普通的加号吗?完全没必要,jvm已经很智能了,可以替我们优化

String str = "abc";
String str1 = str + "d" + "e";

这是一个普通的字符拼接,通过javap -v [类名]命令后 可以看到(无关的因素已隐去):

...
 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String abc
         2: astore_1
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        10: aload_1
        11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        14: ldc           #6                  // String de
        16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: astore_2
....

可以很清楚的看到main方法中,偏移量为7的那行,jvm很智能的替我们添加了StringBuilder的实例化

字符串缓存

如果你曾经通过 jmap -histo:live [processId]看存活的对象,会发现

   1:         71023        7346320  [C
   2:         25216        2219008  java.lang.reflect.Method
   3:         65094        2083008  java.util.concurrent.ConcurrentHashMap$Node
   4:          8639        1881208  [B
   5:         70823        1699752  java.lang.String
   6:         14760        1630288  java.lang.Class
   7:          8695        1064200  [I
   8:         14997         977112  [Ljava.lang.Object;
   9:         19164         919872  org.aspectj.weaver.reflect.ShadowMatchImpl
  10:         20583         823320  java.util.LinkedHashMap$Entry
  ....

字符串对象和map对象常年名列前矛,而后者管理不善更是gc泄漏的主要原因之一...
java6之后String提供了intern方法来对其进行缓存,减少String实例,例如:

String str = new String("a") + new String("bc");
String str1 = "abc";
System.out.println(str == str1);
-----------------------
false

String str = new String("a") + new String("bc");
str.intern();
String str2 = "abc";
System.out.println(str == str2);
-----------------------
true

Java 6的时候被缓存的字符串存储于永久带中,这个空间有限且几乎不被gc照顾,很容易发生OOM,后续版本中,这个参数被放到了堆中,这样就解决了这个问题。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容