失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Java编程性能调优-01|字符串性能优化不容小觑 百M内存轻松存储几十G数据

Java编程性能调优-01|字符串性能优化不容小觑 百M内存轻松存储几十G数据

时间:2022-04-12 09:26:07

相关推荐

Java编程性能调优-01|字符串性能优化不容小觑 百M内存轻松存储几十G数据

一、字符串性能优化不容小觑,百M内存轻松存储几十G数据

一.String 对象是如何实现的?

String str1= "abc";String str2= new String("abc");String str3= str2.intern();assertSame(str1==str2);assertSame(str2==str3);assertSame(str1==str3)

1、String str1 = "abc";通过字面量的方式创建,abc存储于字符串常量池中;2、String str2 = new String("abc");通过new对象的方式创建字符串对象,引用地址存放在堆内存中,abc则存放在字符串常量池中;所以str1 == str2?显然是false3、String str3 = str2.intern();由于str2调用了intern()方法,会返回常量池中的数据,地址直接指向常量池,所以str1 == str3;而str2和str3地址值不等所以也是false(str2指向堆空间,str3直接指向字符串常量池)。

二.String 对象的不可变性

代码中 String 类被 final 关键字修饰,变量 char 数组也被 final 修饰。

被final 修饰代表该类不可继承,而 char[]被 final+private 修饰,代表了 String 对象不可被更改。Java 实现的这个特性叫作 String 对象的不可变性,即 String 对象一旦创建成功,就不能再对它进行改变。

1、不可被更改的优点

第一,保证 String 对象的安全性。假设 String 对象是可变的,那么 String 对象将可能被恶意修改。

第二,保证 hash 属性值不会频繁变更,确保了唯一性,使得类似 HashMap 容器才能实现相应的 key-value 缓存功能。

第三,可以实现字符串常量池。在 Java 中,通常有两种创建字符串对象的方式,一种是通过字符串常量的方式创建,如 String str=“abc”;另一种是字符串变量通过 new 形式的创建,如 String str = new String(“abc”)。

代码中使用第一种方式创建字符串对象时,JVM 首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。String str = new String(“abc”) 这种方式,首先在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,“abc"将会在常量池中创建;其次,在调用 new 时,JVM 命令将会调用 String 的构造函数,同时引用常量池中的"abc” 字符串,在堆内存中创建一个 String 对象;最后,str 将引用 String 对象。

三.String 对象的优化

1、如何构建超大字符串?

尽量的使用StringBuilder或者StringBuffer

用String进行拼接的,即使已经被编译器优化成了StringBuilder,但是每次循环都会生成一个新的 StringBuilder 实例,同样也会降低系统的性能。

如果在多线程编程中,String 对象的拼接涉及到线程安全,你可以使用 StringBuffer。但是要注意,由于 StringBuffer 是线程安全的,涉及到锁竞争,所以从性能上来说,要比 StringBuilder 差一些。

2、使用 String.intern 节省内存

String.intern是一个Native方法,主要作用是将一个对象放入常量池(方法区、元空间)

在每次赋值的时候使用 String 的 intern 方法,如果常量池中有相同值,就会重复使用该对象,返回对象引用,这样一开始的对象就可以被回收掉。这种方式可以使重复性非常高的地址信息存储大小从 20G 降到几百兆。

下面是一些案例

String a =new String("abc").intern();String b = new String("abc").intern();if(a==b) {System.out.print("a==b");}a和b虽然都把abc放入了常量池(方法区、元空间),但是a和b都指向的是堆中的对象,所以返回false。

String s3 = new String("1") + new String("1") ;s3.intern();String s4 = "11";System.out.println(s3 == s4);s3虽然是“11”,但是并不是堆区中的对象,因为是两个new出来的拼接而成的。s3.intern()是将“11”放入了常量池因为常量池中存在“11”,所以s3==s5=true。

String str2 = new String("str") + new String("01");String str1 = "str01";String str3 = str2.intern();System.out.println(str3 == str1);常量池中有了str1的对象,所以 str2.intern()放入的步骤是无效的,赋值的步骤是有效的,都取自常量池,返回ture。

String str2 = new String("str") + new String("01");str2.intern();String str1 = "str01";System.out.println(str2 == str1);str2和str1都取自常量池,因为str2是堆对象拼接而成的不是真正的堆对象,返回ture。String str3 = new String("str01");str3.intern();String str4 = "str01";System.out.println(str3 == str4);返回false

3、如何使用字符串的分割方法(尽量使用index替代Split())

Split() 方法使用了正则表达式实现了其强大的分割功能,而正则表达式的性能是非常不稳定的,使用不恰当会引起回溯问题,很可能导致 CPU 居高不下。

所以应该慎重使用 Split() 方法,可以用 String.indexOf() 方法代替 Split() 方法完成字符串的分割。如果实在无法满足需求,在使用 Split() 方法时,对回溯问题加以重视就可以了。

如果觉得《Java编程性能调优-01|字符串性能优化不容小觑 百M内存轻松存储几十G数据》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。