场景 : 最近,有客户反应某些功能执行得很慢,我们于是对代码日志进行了定位,我们的系统架构是nginx+tomcat; 我们可以直接定位到tomcat的catalina日志,但是后来吧,我们这边统一要整理响应时间超过5S以上的,对这些都要进行整改;所以我们先直接分析nginx的日志文件,查看请求跟响应超过5S以上的统计出来,然后在tomcat的日志当中定位这些请求,查找到具体的时间,以及上下文,最后我们定位到某个方法执行超过一些时间的。现在,我们抛开掉业务场景,从最近本的优化方案看。
目录
1)普通变量 改为 寄存器变量
转载来于:https://blog.csdn.net/u011236602/article/details/81092504
i++ 改为 ++i;
int i=0, j;
j=++i; //前置版本,运算对象先自增1,然后将改变后的对象作为求值结果,再赋值给j;
j=i++; //后置版本,先赋值给j;再运算对象自增1,但求值结果是运算对象改变之前那个值的副本.
2)拆分操作原则
对于一个可结合和可交换的合并操作来说,比如整数的加法或乘法,
我们可以通过将一组合并操作分割成 2 个或更多的部分,并在最后合并结果来提高性能。
原理:
普通代码只能利用 CPU 的一个寄存器,分割后可以利用多个寄存器。
当分割达到一个数量时,寄存器用完,性能不再提升,甚至会开始下降。
用代码来描述,如下:
// 一般情况下的代码 for (i = 1; i < n+1; i++)
{
res = res OPER i;
}// 循环分割后代码for (i = 1; i < n; i+=2)
{
res1 = res1 OPER i;
res2 = res2 OPER (i+1);
}
int 整数加法,性能测试结果对比如下:
整数的加法,普通代码运行 26s,循环分割后,18s。
浮点数计算的性能提升,明显大于整数,乘法的性能提升,略大于加法。
3)外小内大”原则
转自:https://blog.csdn.net/qq_44750696/article/details/121394877
嵌套循环应该遵循“外小内大”的原则,当需要嵌套循环时,尽可能让外层循环越小,性能越好;
long stratTime = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
for (int j = 0; j < 10; j++) {
}
}
long endTime = System.nanoTime();
System.out.println("外大内小耗时:"+ (endTime - stratTime));
//改为
long stratTime = System.nanoTime();
for (int i = 0; i <10 ; i++) {
for (int j = 0; j < 10000000; j++) {
}
}
long endTime = System.nanoTime();
System.out.println("外小内大耗时:"+(endTime - stratTime));
两者对比:
外大内小耗时:200192114
外小内大耗时:97995997
4)提取与循环无关的表达式
//代码中的a*b运算和循环是无关的,所以我们应该把他放到循环的外面,避免重复计算。
for (int i = 0; i < 10000000; i++) {
i=i*a*b;
}
//应修改为:
c = a*b;
for (int i = 0; i < 10000000; i++) {
i=i*c;
}
代码中的a*b运算和循环是无关的,所以我们应该把他放到循环的外面,避免重复计算,这可是不容忽视的效率问题
5)消除循环终止判断时的方法调用
long stratTime = System.nanoTime();
for (int i = 0; i < list.size(); i++) {
}
long endTime = System.nanoTime();
System.out.println("未优化list耗时:"+(endTime - stratTime));
//应改为:
long stratTime = System.nanoTime();
int size = list.size();
for (int i = 0; i < size; i++) {
}
long endTime = System.nanoTime();
System.out.println("优化list耗时:"+(endTime - stratTime));
两者耗时对比:
未优化list耗时:27375
优化list耗时:2444
list.size()每次循环都会被执行一次,这无疑会影响程序的性能,所以应该将其放到循环外面,用一个变量来代替,优化前后的对比也很明显。
6)异常捕获
long stratTime = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
try {
} catch (Exception e) {
}
}
long endTime = System.nanoTime();
System.out.println("在内部捕获异常耗时:"+(endTime - stratTime));
//应改为:
long stratTime = System.nanoTime();
try {
for (int i = 0; i < 10000000; i++) {
}
} catch (Exception e) {
}
long endTime = System.nanoTime();
System.out.println("在外部捕获异常耗时:"+(endTime - stratTime));
两者耗时对比:
在内部捕获异常耗时:12150142
在外部捕获异常耗时:1955
捕获异常是很耗资源的,所以不要把try catch放到循环内部,优化后同样有好几个数量级的提升。
7)尝试使用map结构查询的执行方式;
for(Member m2:list2){
if(m2.getName()==null){
for(Member m1:list1){
if(m1.getId().intValue()==m2.getId().intValue()){
System.out.println(m2.getId()+" Name 值为空!!!");
}
}
}
}
//下边来看使用map代替的执行方式,以及两种方式的效率对比:
//map查询测试
long s3 = System.currentTimeMillis();
int mapNumber = 0;
Map<Integer, Member> map = new HashMap<>();
for(Member m1:list1){
map.put(m1.getId(), m1);
}
for(Member m2:list2){
if(m2.getName()==null){
Member m = map.get(m2.getId());
if(m!=null){
System.out.println(m2.getId()+" Name 值为空!!!");
mapNumber++;
}
}
}
8)减少循环变量的实例化,比如:
for (int i = 0; i < 1000; i++){}
应改为:
int i, j, k;
for (i = 0; i < 10; i++){}