String 既然能这样性能调优我直呼内行.docx

上传人:安*** 文档编号:73264376 上传时间:2023-02-17 格式:DOCX 页数:22 大小:19.80KB
返回 下载 相关 举报
String 既然能这样性能调优我直呼内行.docx_第1页
第1页 / 共22页
String 既然能这样性能调优我直呼内行.docx_第2页
第2页 / 共22页
点击查看更多>>
资源描述

《String 既然能这样性能调优我直呼内行.docx》由会员分享,可在线阅读,更多相关《String 既然能这样性能调优我直呼内行.docx(22页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、String既然能这样性能调优,我直呼内行莫慌,今天给大家见识一下不一样的String,从根上拿捏直达G点。并且码哥分享一个例子:通过性能调优我们能实现百兆内存轻松存储几十G数据。String对象是我们每天都摸的对象类型,但是她的性能问题我们却总是忽略。爱她,不能只会简单一起玩耍,要深入了解String的内心深处,做一个心有猛虎,细嗅蔷薇的暖男。通过以下几点分析,我们一步步揭开她的衣裳,直达内心深处,提升一个Level,让String直接起飞:字符串对象的特性;String的不可变性;大字符串构建技巧;String.intern节省内存;字符串分割技巧;String身体解密想要深入了解,就先从

2、基本组成开始String缔造者对String对象做了大量优化来节省内存,从而提升String的性能:Java6及之前数据存储在char数组中,String通过offset和count两个属性定位char数据获取字符串。这样可以高效快速的定位并共享数组对象,并且节省内存,但是有可能导致内存泄漏。共享char数组为啥可能会导致内存泄漏呢?String(intoffset,intcount,charvalue)this.value=value;this.offset=offset;this.count=count;publicStringsubstring(intbeginIndex,intendI

3、ndex)/checkboundaryreturnnewString(offset+beginIndex,endIndex-beginIndex,value);调用substring()的时候虽然创建了新的字符串,但字符串的值value仍然指向的是内存中的同一个数组,如下图所示:如果我们仅仅是用substring获取一小段字符,而原始string字符串非常大的情况下,substring的对象如果一直被引用。此时String字符串也无法回收,从而导致内存泄露。如果有大量这种通过substring获取超大字符串中一小段字符串的操作,会因为内存泄露而导致内存溢出。JDK7、8去掉了offset和co

4、unt两个变量,减少了String对象占用的内存。substring源码:publicString(charvalue,intoffset,intcount)this.value=Arrays.copyOfRange(value,offset,offset+count);publicStringsubstring(intbeginIndex,intendIndex)intsubLen=endIndex-beginIndex;returnnewString(value,beginIndex,subLen);substring()通过newString()返回了一个新的字符串对象,在创建新的对象时

5、通过Arrays.copyOfRange()深度拷贝了一个新的字符数组。如下图所示:String.substring方法不再共享char数组的数据,解决了可能内存泄漏的问题。Java9将char字段改为byte,新增coder属性。码哥,为什么这么改呢?一个char字符占2个字节,16位。存储单字节编码内的字符(占一个字节的字符)就显得非常浪费。为了节约内存空间,于是使用了1个字节占8位的byte数组来存放字符串。勤俭节约的女神,谁不爱新属性coder的作用是:在计算字符串长度或者使用indexOf()方法时,我们需要根据编码类型来计算字符串长度。coder的值分别表示不同编码类型:0:表示使

6、用Latin-1(单字节编码);1:使用UTF-16。String的不可变性了解了String的基本组成之后,发现String还有一个比外在更性感的特性,她被final关键字修饰,char数组也是。我们知道类被final修饰代表该类不可继承,而char被final+private修饰,代表了String对象不可被更改。String对象一旦创建成功,就不能再对它进行改变。final修饰的好处安全性当你在调用其他方法时,比如调用一些系统级操作指令之前,可能会有一系列校验。如果是可变类的话,可能在你校验过后,它的内部的值又被改变了,这样有可能会引起严重的系统崩溃问题。高性能缓存String不可变之后

7、就能保证hash值得唯一性,使得类似HashMap容器才能实现相应的key-value缓存功能。实现字符串常量池由于不可变,才得以实现字符串常量池。字符串常量池指的是在创建字符串的时候,先去常量池查找是否创建过该字符串;如果有,则不会开辟新空间创建字符串,而是直接把常量池中该字符串的引用返回给此对象。创建字符串的两种方式:Stringstr1=“码哥字节;Stringstr2=newString(“码哥字节);当代码中使用第一种方式创建字符串对象时,JVM首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用。否则新的字符串将在常量池中被创建,并返回该引用。这样可以减少同一个值的字符串

8、对象的重复创建,节约内存。第二种方式创建,在编译类文件时,码哥字节字符串将会放入到常量结构中,在类加载时,“码哥字节将会在常量池中创建;在调用new时,JVM命令将会调用String的构造函数,在堆内存中创建一个String对象,同时该对象指向常量池中的“码哥字节字符串,str指向刚刚在堆上创建的String对象;如下图(str1、str2):什么是对象和对象引用呀?str属于方法栈的字面量,它指向堆中的String对象,并不是对象本。对象在内存中是一块内存地址,str则是指向这个内存地址的引用。也就是说str并不是对象,而只是一个对象引用。码哥,字符串的不可变到底指的是什么呀?Strings

9、tr=Java;str=Java,yyds第一次赋值Java,第二次赋值Java,yyds,str值确实改变了,为什么我还说String对象不可变呢?这是因为str只是String对象的引用,并不是对象本身。真正的对象依然还在内存中,没有被改变。优化实战了解了String的对象实现原理和特性,是时候要深入女神内心,结合实际场景,如何更上一层楼优化String对象的使用。大字符串如何构建既然String对象是不可变,所以我们在频繁拼接字符串的时候是否意味着创建多个对象呢?Stringstr=癞蛤蟆撩青蛙+长的丑+玩的花;是不是以为先生成癞蛤蟆撩青蛙对象,再生成癞蛤蟆撩青蛙长的丑对象,最后生成癞蛤

10、蟆撩青蛙长得丑玩的花对象。实际运行中,只有一个对象生成。这是为什么呢?虽然代码写的丑陋,但是编译器自动优化了代码。再看下面例子:Stringstr=小青蛙;for(inti=;i<1000;i+)str+=i;上面的代码编译后,你可以看到编译器同样对这段代码进行了优化。Java在进行字符串的拼接时,偏向使用StringBuilder,这样可以提高程序的效率。Stringstr=小青蛙;for(inti=;i<1000;i+)str=(newStringBuilder(String.valueOf(str).append(i).toString();即使如此,还是循环内重复创建Str

11、ingBuilder对象。敲黑板所以做字符串拼接的时候,我建议你还是要显式地使用StringBuilder来提升系统性能。如果在多线程编程中,String对象的拼接涉及到线程安全,你可以使用StringBuffer。运用intern节省内存直接看intern()方法的定义与源码:intern()是一个本地方法,它的定义中说的是,当调用intern方法时,如果字符串常量池中已经包含此字符串,则直接返回此字符串的引用。否则将此字符串添加到常量池中,并返回字符串的引用。如果不包含此字符串,先将字符串添加到常量池中,再返回此对象的引用。什么情况下适合使用intern()方法?Twitter工程师曾分享

12、过一个String.intern()的使用示例,Twitter每次发布消息状态的时候,都会产生一个地址信息,以当时Twitter用户的规模预估,服务器需要20G的内存来存储地址信息。publicclassLocationprivateStringcity;privateStringregion;privateStringcountryCode;privatedoublelongitude;privatedoublelatitude;考虑到其中有很多用户在地址信息上是有重合的,比如,国家、省份、城市等,这时就可以将这部分信息单独列出一个类,以减少重复,代码如下:publicclassShared

13、LocationprivateStringcity;privateStringregion;privateStringcountryCode;publicclassLocationprivateSharedLocationsharedLocation;doublelongitude;doublelatitude;通过优化,数据存储大小减到了20G左右。但对于内存存储这个数据来说,依然很大,怎么办呢?Twitter工程师使用String.intern()使重复性非常高的地址信息存储大小从20G降到几百兆,从而优化了String对象的存储。核心代码如下:SharedLocationsharedLo

14、cation=newSharedLocation();sharedLocation.setCity(messageInfo.getCity().intern();sharedLocation.setCountryCode(messageInfo.getRegion().intern();sharedLocation.setRegion(messageInfo.getCountryCode().intern();弄个简单例子方便理解:Stringa=newString(abc).intern();Stringb=newString(abc).intern();System.out.print(a

15、=b);输出结果:true。在加载类的时候会在常量池中创建一个字符串对象,内容是abc。创建局部a变量时,调用newSting()会在堆内存中创建一个String对象,String对象中的char数组将会引用常量池中字符串。在调用intern方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。创建b变量时,调用newSting()会在堆内存中创建一个String对象,String对象中的char数组将会引用常量池中字符串。在调用intern方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用给局部变量。而刚在堆内存中的两个对象,由于没有引用指向它,将会被垃圾回

16、收。所以a和b引用的是同一个对象。字符串分割有妙招Split()方法使用了正则表达式实现了其强大的分割功能,而正则表达式的性能是非常不稳定的。使用不恰当会引起回溯问题,很可能导致CPU居高不下。Java正则表达式使用的引擎实现是NFA(NondeterministicFiniteAutomaton,确定型有穷自动机)自动机,这种正则表达式引擎在进行字符匹配时会发生回溯(backtracking),而一旦发生回溯,那其消耗的时间就会变得很长,有可能是几分钟,也有可能是几个小时,时间长短取决于回溯的次数和复杂度。所以我们应该慎重使用Split()方法,我们可以用String.indexOf()方法代替Split()方法完成字符串的分割。总结与思考我们从String进化历程掌握了她的组成,不断的改变成员变量节约内存。她的不可变性从而实现了字符串常量池,减少同一个字符串的重复创建,节约内存。但也是因为这个特性,我们在做长字符串拼接时,需要显示使用StringBuilder,以提高字符串的拼接性能。最后,在优化方面,我们还可以使用intern方法,让变量字符串对象重复使用常量池中相同值的对象,进而节约内存。以上文章;于码哥字节,作者码哥

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 工程图纸

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁