洞察移动政务小程序助力政府数字化转型,保障数据安全和效率提升
807
2022-09-11
虚拟机系列:字符串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
字符串常量池和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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~