性能调优 -- Java编程中的性能优化
String作为我们使用最频繁的一种对象类型,其性能问题是最容易被忽略的。作为Java中重要的数据类型,是内存中占据空间比较大的一个对象。如何高效地使用字符串,可以帮助我们提升系统的整体性能。
现在,我们就从String对象的实现、特性以及实际使用中的优化这几方面来入手,深入理解以下String的性能优化。
在这之前,首先看一个问题。通过三种方式创建三个对象,然后依次两两匹配,得出的结果是什么?答案留到最后揭晓。
String str1 = "abc";
String str2 = new String("abc");
String str3 = str2.intern();
System.out.println(str1 == str2);
System.out.println(str2 == str3);
System.out.println(str1 == str3);
String对象是如何实现的?
Java中对String对象做了大量的优化,以此来节约内存空间,提升String对象的性能。下图是Java6 -> Java9 String对象属性的变化:
可以看到,String的属性有了以下的变化:
- 在Java6及以前的版本中,String对象是对char数组进行了封装实现的对象,主要有:char数组、偏移量offset、字符数量count、哈希值hash。String对象通过offset和count属性来定位char数组,获取字符串。这样做可以高效快速地共享数组对象,能节省内存空间,但容易出现内存泄漏。
- 从Java7到Java8版本,Java对String做了一些改变。String类中不再有offset和count两个属性了。这样做可以使String对象占用的内存减少,并且String.substring方法也不再共享char[],解决了可能出现的内存泄漏的问题。
- 从Java9版本开始,将char[]改成了byte[],并增加了新属性coder,coder是一个编码格式的标识。
为什么要这么改呢?
我们知道,一个char字符占16位,2个字节。这种情况下存储单字节的字符就很容易浪费了。JDK1.9的String类为了节省内存空间,就使用了占8位,1个字节的byte数组来存储字符串。
coder属性的作用是:在计算字符串长度或者使用indexOf()时,需要根据这个字段,判断如何计算字符串的长度。coder属性值默认有0和1两个值,0代表Latin-1(单字节编码),1代表UTF-16。如果String判断字符串只包含Latin-1,则coder值取0,反之为1。
String对象的不可变性
如果看过String的源码,就会发现,String类是被final关键字修饰的,且变量char数组也被final修饰。
一个类被final修饰代表着该类不可继承,char[]被private和final修饰着,代表String对象不可被更改。这就叫做String对象的不可变性。即如果String对象一旦创建成功了,就不能再对它进行改变。
这样做的好处在哪里?
第一、保证了String对象的安全性。假设String对象是可变的,那么String对象就会被恶意修改。
第二.、保证hash属性值不会频繁变更,确保了唯一性。使得类似HashMap容器才能实现相应的key-value缓存功能。
第三、可以实现字符串常量池。Java中,通常有2种创建字符串对象的方式,一种是通过字符串常量的方式创建,如String str = "abc";另一种是字符串常量通过new形式的创建,如String str = new String("abc")。
当代码中使用第一种方式创建字符串对象时,JVM首先检查该对象是否在字符串常量池中,如果在就返回该对象的引用,否则新的字符串将在常量池中创建。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
第二种方式,首先在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,"abc"会在常量池中创建;然后调用new时,JVM命令将会调用String的构造函数,同时引用常量池的"abc"字符串,在堆内存中创建一个String对象,最后str引用String对象。
String对象的优化
1.如何构建超大字符串
编程过程中字符串的拼接很常见。如果使用String对象相加,拼接我们想要的字符串,会不会产生多个对象呢?比如说以下代码:
String str = "ab" + "cd" + "ef";
分析代码可知:首先会生成ab对象,再生成abcd对象,最后生成abcdef对象。理论上说,代码很低效。
但实际上,会发现只有一个对象生成,这是为什么呢?编译时编译器会自动帮我们优化代码,使得最后只得出一个对象“abcdef”。
再来看看,如果进行字符串常量的累计,又会出现什么结果?
String str = "abcdef";
for (int i = 0; i < 100; i++) {
str = str + i;
}
上面的代码编译后,编译器同样对代码进行了优化,在进行字符串拼接时,偏向使用StringBuilder,这样可以提升效率。上面的代码变成了下面这样:
String str = "abcdef";
for (int i = 0; i < 100; i++) {
str = (new StringBuilder(String.valueOf(str))).append(i).toString();
}
总结:即使使用+号作为字符串的拼接,一样可以被编译器优化成StringBuilder的方式。但如果每次循环都生成一个新的StringBuilder实例,同样会降低系统的性能。所以平时做字符串拼接的时候,建议还是显示使用StringBuilder来提升性能。在多线程编程时,String对象的拼接涉及到了线程安全,可以使用StringBuffer。但由于StringBuffer是线程安全的,涉及到锁竞争,所以就性能上来说会比StringBuilder差些。
2.如何使用String.intern节省内存?
对于一些数据,数据量非常大,但同时又有大部分重合的,该如何处理呢?
具体做法是,每次赋值的时候使用String的intern方法,如果常量池中有相同值,就会重复使用该对象,返回对象的引用,这样一开始的对象就可以被回收掉了,这样的话数据量就会大幅度降低了。
我们再来看一个例子:
String a = new String("abc").intern();
String b = new String("abc").intern();
if (a == b) {
System.out.println("a == b");
}
输出结果是: a == b
在字符串常量池中,默认会将对象放入常量池;在字符串变量中,对象总是创建在堆内存的,同时也会在常量池中创建一个字符串对象,复制到堆内存对象中,并返回堆内存对象引用。
如果调用intern方法,会去查看字符串常量池中是否有等于该对象的字符串,如果没有,就会在常量池中新增该对象,并返回该对象引用;如果有则返回常量池中的字符串引用。堆内存中原有的对象由于没有引用指向它,将会通过垃圾回收器回收。
3.如何使用字符串的分割方法?
spilt()方法使用了正则表达式实现了强大的分割功能,而正则表达式的性能是非常不稳定的,使用不当会引起回溯问题,很可能导致CPU居高不下。
所以要慎重使用spilt方法,我们可以用String.indexOf()方法代替spilt()方法完成字符串的分割,如果实在无法满足需求,就在使用spilt方法时,对回溯问题加以重视就可以了。
总结
通过上面的叙述,我们认识到了做好String字符串性能的优化,可以提升整个系统的性能。在这个理论基础上,Java版本在迭代中不断更改成员变量,节约内存空间,对String性能进行了优化。
我们还提到了String对象的不可变性,正是这个特性实现了字符串常量池,通过减少同一个值的字符串对象的重复创建,进一步节约内存。也是因为这个特性,我们在做长字符串的拼接时,需要显示使用StringBuilder,以提升字符串的拼接性能。最后在优化方面,我们还可以使用intern方法,让变量字符串对象重复使用常量池中相同值的对象,进而节约内存。
最后,公布上面那道题的结果:
false、false、true。
其中, String str1 = “abc”;通过字面量的方式创建,abc存储于字符串常量池中;
String str2 = new String("abc");通过new对象的方式创建字符串对象,引用地址存放在堆内存中,abc则存放在字符串常量池中,所以为false;
String str3 = str2.intern();由于调用了intern()方法,会返回常量池中的数据,str3此时就指向常量池中的abc,和str1的方式一样,所以为true;
性能调优 -- Java编程中的性能优化的更多相关文章
- 性能调优之访问日志IO性能优化
性能调优之访问日志IO性能优化 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821 ...
- OCM_第十五天课程:Section6 —》数据库性能调优 _SQL 访问建议 /SQL 性能分析器/配置基线模板/SQL 执行计划管理/实例限制
注:本文为原著(其内容来自 腾科教育培训课堂).阅读本文注意事项如下: 1:所有文章的转载请标注本文出处. 2:本文非本人不得用于商业用途.违者将承当相应法律责任. 3:该系列文章目录列表: 一:&l ...
- Java编程中“为了性能”尽量要做到的一些地方
最近的机器内存又爆满了,除了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了. 下面是参考网络资源总结的一些在Ja ...
- Java性能调优:利用JMC分析性能
Java性能调优作为大型分布式系统提供高性能服务的必修课,其重要性不言而喻. 好的分析工具能起到事半功倍的效果,利用分析利器JMC.JFR,可以实现性能问题的准确定位. 本文主要阐述如何利用JMC分析 ...
- Java性能调优:利用JFR生成性能日志
Java性能调优作为大型分布式系统提供高性能服务的必修课,其重要性不言而喻. 好的分析工具能起到事半功倍的效果,利用分析利器JMC.JFR,可以实现性能问题的准确定位. 本文主要阐述如何利用JFR生成 ...
- java编程中'为了性能'一些尽量做到的地方
原文地址:http://blog.csdn.NET/m13666368773/article/details/7796924 最近的机器内存又爆满了,出了新增机器内存外,还应该好好review一下我们 ...
- 性能调优3:硬盘IO性能
数据库系统严重依赖服务器的资源:CPU,内存和硬盘IO,通常情况下,内存是数据的读写性能最高的存储介质,但是,内存的价格昂贵,这使得系统能够配置的内存容量受到限制,不能大规模用于数据存储:并且内存是易 ...
- Java编程中“为了性能”需做的26件事
1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: (1)控制资源的使用,通过线程同步来控制 ...
- 【转载】java编程中'为了性能'一些尽量做到的地方
1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面 第一,控制资源的使用,通过线程同步来控制资 ...
随机推荐
- U盘中病毒无法更改U盘隐藏属性,文件变成快捷方式
问题:U盘中病毒了文件都变成快捷方式了,文件被隐藏:隐藏属性无法修改 解决方法: 1.首先给U盘杀毒[如360杀毒] 2.修改U盘隐藏属性:如下图 经过此步骤被隐藏的文件可以被显示出来了.但是U盘里的 ...
- 求局域网内所有在线主机的ip
在一个局域网内,已知其中一台主机的ip为192.,子网掩码为255.,求所有其他在线主机的ip. shell 编码实现 #!/bin/bash netWorkIP=. ) do $netWorkIP$ ...
- 16.Django学习之文件上传和下载
上传就这么六步! 一. settings配置文件中配置 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'medias').repl ...
- luogu P1731 [NOI1999]生日蛋糕 |暴力枚举
题目背景 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层 生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1<=i<=M)层蛋糕是半径为Ri, 高度为Hi的圆柱 ...
- iOS开发-Apple Pay-苹果支付
转自:http://www.open-open.com/lib/view/open1471952847228.html Apple Pay,是苹果公司在2014年苹果秋季新品发布会上发布的一种基于NF ...
- [TimLinux] Python3.6 异常继承关系
Python3.6 异常继承结构 object └── BaseException ├── Exception │ ├── ArithmeticError │ │ ├── Floating ...
- Pandas里面常用的一些数据分析函数总结
import pandas as pdimport numpy as np pandas 有两个主要的数据结构:Series 和 DataFrame:Series 是一个一维数组对象 ,它包含一组索引 ...
- ThreadLocal的进化——TransmittableThreadLocal
上一篇文章中,我们谈到了 InheritableThreadLocal,它解决了 ThreadLocal 针对父子线程无法共享上下文的问题.但我们可能听说过阿里的开源产品TransmittableTh ...
- 十大C++实战项目,你会几个?【高薪必备】
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:实验楼 市面上有很多C++的实战项目,从简单到进阶,学习每个项目都 ...
- 基于 asm 实现比 spring BeanUtils 性能更好的属性拷贝框架
Bean-Mapping 日常开发中经常需要将一个对象的属性,赋值到另一个对象中. 常见的工具有很多,但都多少不够简洁,要么不够强大. 我们经常使用的 Spring BeanUtils 性能较好,但是 ...