虚拟机系列:字符串String

网友投稿 807 2022-09-11

虚拟机系列:字符串String

虚拟机系列:字符串String

本文是JDK1.8

String 字符串是开发中最常用的一种对象。在Java中,String虽然不是基本数据类型,但是也和基本数据类型一样。

String对象的特点

String对象有三个基本特点:不变性,常量池,类的final定义

不变性

不变性是指String对象一旦生成就不会再被改变。这个特性可以泛化成不变(immutable)模式,即一个对象的状态在对象被创建之后就不再发生变化。不变模式的主要作用是 当一个对象需要在多个线程中共享,并且访问频繁时,可以避免同步和锁等待的时间,从而大幅提高系统效率。

不变性可以提高多线程的访问性能,因为对象不可变,所有在多线程访问的时候都是只读的,不加同步也不会产生数据的不一致,从而减少系统的开销。

因为String具有不变性,所以我们在系统中对其的修改其实都是创建新的字符串实现的。比如​​String.substring()​​​,​​String.concat()​​​等方法,都没有直接修改原字符串,而是在内存中生成新的字符串,然后把引用地址指向原来的持有对象。如果真的要修改一个对象可以使用 ​​StringBuffer​​​ 或 ​​StringBuilder​​

注意:StringBuffer和StringBuilder的主要区别在于前者是线程安全的,可用于多线程中,但是效率相对会降低。

字符串在常量池中的优化

针对常量池的优化是指如果两个String对象拥有相同的值,他们只引用常量池中同一个拷贝。好处就是当这个字符串反复出现的时候 可以大幅节减内存空间。

String str1 = new String("纪先生");String str2 = new String("纪先生");System.out.println(str1==str2); // falseSystem.out.println(str1==str2.intern()); // falseSystem.out.println("纪先生"==str2.intern()); // trueSystem.out.println(str1.intern()==str2.intern()); // true

如上述代码中的str1,str2两个字符串各自开辟了一块堆空间存放String实例,如下图。

虽然str1,str2的内容相同,但是在堆中的引用是不同的。

而String.intern()返回的是字符串在常量池中的引用,所以和str1是不一样的。

​​"纪先生"==str2.intern()​​ 的相同,说明String.intern()始终和常量池相等,

所以最后一个两者都是引用的常量池,所以也是一样的。

final修饰的String

如下是String的源码部分,String是用final修饰的。而使用final关键字修饰的String是不能有任何子类的,这是对系统安全性的保证。这是语言设计者精心设计的,没必要也不允许被改变。

public final class String implements java.io.Serializable, Comparable, CharSequence { /** 字符串实际上就是用final关键字修饰的char数组. */ private final char value[];}

字符串常量池和String.intern()

在虚拟机中有一块常量池的区域专门用于存放字符串常量的,在jdk1.6及以前的版本都是存放在永久区的,但是从jdk1.7开始存放在堆中。

而String.intern()是一个native方法,就是获取常量池中的字符串引用。如下图jdk1.8的注释可以看出:当使用intern()方法时,如果常量池中已经有这个字符串了,则把这个字符串返回。如果常量池中没有,则把这个字符串加入到常量池,然后把它的引用返回。

虽然String.intern()返回值永远等于字符串常量,但存在一种情况:一次intern调用之后该字符串在某一个时刻被回收,之后再次调用intern,字面量相同的字符串重新被加入常量池,但是引用位置已经改变;

String str = "000";System.out.println(System.identityHashCode(( str+Integer.toString(0) )));System.out.println(System.identityHashCode(( str+Integer.toString(0) ).intern()));System.gc();System.out.println(System.identityHashCode(( str+Integer.toString(0) ).intern()));

返回

14067182182452574101705736037

如上三次hash值的输出各不相同,第一次是字符串本身,第二次是常量池的引用,第三次也是常量池的引用,但是这两次之间有次垃圾回收,所以第三次和第二次使用的不是同一个常量池的引用。如果去除System.gc()那么返回的hash值就会是一样的,如下

1406718218245257410245257410

JVM虚拟机系列历史文章

1. 虚拟机系列:jvm运行时堆内存如何分代;

2. 虚拟机系列:jvm中的垃圾回收算法;

3. 虚拟机系列:jvm运行时数据区域;

4. 虚拟机系列:JVM中对象的创建,内存布局和访问定位;

5. 虚拟机系列:JVM中的垃圾收集器;

6. 虚拟机系列:JVM中的内存分配;

7. 虚拟机系列:搞懂虚拟机的日志和日志参数;

8. 虚拟机系列:虚拟机性能监控基础工具;

9. 虚拟机系列:虚拟机性能监控基础工具-jstat;

10. 虚拟机系列:性能监控可视化工具-JConsole;

11. 虚拟机系列:图形化监控工具-VisualVM;

12. 虚拟机系列:内存溢出OOM以及解决思路;

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:用Python复制文件的9个方法
下一篇:虚拟机系列:内存溢出OOM以及解决思路
相关文章

 发表评论

暂时没有评论,来抢沙发吧~