.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1{font-size:30px;margin-bottom:5px}.markdown-body h2{padding-bottom:12px;font-size:24px;border-bottom:1px solid #ececec}.markdown-body h3{font-size:18px;padding-bottom:0}.markdown-body h4{font-size:16px}.markdown-body h5{font-size:15px}.markdown-body h6{margin-top:5px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body img{max-width:100%}.markdown-body hr{border:none;border-top:1px solid #ddd;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;border-radius:2px;overflow-x:auto;background-color:#fff5f5;color:#ff502c;font-size:.87em;padding:.065em .4em}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{text-decoration:none;color:#0269c8;border-bottom:1px solid #d1e9ff}.markdown-body a:active,.markdown-body a:hover{color:#275b8c}.markdown-body table{display:inline-block!important;font-size:12px;width:auto;max-width:100%;overflow:auto;border:1px solid #f6f6f6}.markdown-body thead{background:#f6f6f6;color:#000;text-align:left}.markdown-body tr:nth-child(2n){background-color:#fcfcfc}.markdown-body td,.markdown-body th{padding:12px 7px;line-height:24px}.markdown-body td{min-width:120px}.markdown-body blockquote{color:#666;padding:1px 23px;margin:22px 0;border-left:4px solid #cbcbcb;background-color:#f8f8f8}.markdown-body blockquote:after{display:block;content:""}.markdown-body blockquote>p{margin:10px 0}.markdown-body ol,.markdown-body ul{padding-left:28px}.markdown-body ol li,.markdown-body ul li{margin-bottom:0;list-style:inherit}.markdown-body ol li .task-list-item,.markdown-body ul li .task-list-item{list-style:none}.markdown-body ol li .task-list-item ol,.markdown-body ol li .task-list-item ul,.markdown-body ul li .task-list-item ol,.markdown-body ul li .task-list-item ul{margin-top:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:3px}.markdown-body ol li{padding-left:6px}.markdown-body .contains-task-list{padding-left:0}.markdown-body .task-list-item{list-style:none}@media (max-width:720px){.markdown-body h1{font-size:24px}.markdown-body h2{font-size:20px}.markdown-body h3{font-size:18px}}
大家好,我是南橘,从接触java到现在也有差不多两年时间了,两年时间,从一名连java有几种数据结构都不懂超级小白,到现在懂了一点点的进阶小白,学到了不少的东西。知识越分享越值钱,我这段时间总结(包括从别的大佬那边学习,引用)了一些平常学习和面试中的重点(自我认为),希望给大家带来一些帮助
上一章介绍了字符串、数字和集合类的一些高效用法,这一章就继续查漏补缺、介绍更多的性能优化技巧。
从之前的文章可以得知,int到String的转换是一个耗时的操作,因为我们需要尽量避免做这些转换。如果实在需要,也可以动用上期Integer自动拆包装包的方法,预先将一部分的int值转化为字符串。
我们通过工具类,预先设定好1024个缓存(或者根据业务设置更多),所有调用int2String方法的时候都会预先判断数据是否在缓存内,如果小于1024,则会去调用缓存数据。
测试代码如下,我应该会在下下一章详细介绍我如何通过JMH来对代码性能进行测试
*
大家注意一下这三张图片Benchmark区域的信息不难发现,在数字1024以内,使用缓存的int2StringByCache的性能几乎高出int2String一个数量级。
这里就是运用了JDK对INTEGER自动拆箱装箱的原理
Native方法就是调用一个非Java代码的接口。
我在之前的文章【进阶之路】攻克JVM——JVM的垃圾回收机制(二)里有讲过。
一般来说,作为java的底层代码,Native有着更好的性能。
最常用的Native方法是Stream.arraycopy方法,把原数组的内容复制到目标数组中:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
里面还有各种各样的方法,大家可以根据情况来使用。
在条件判断中,如果有较多的分支判断,使用switch语句通常比使用if语句的效率更高。if语句会每次取出变量进行比较从而确定处理分支,而switch语句只需要取出一次变量,然后根据tableswitch直接找到分支即可。
根据测试,分支较少的情况下,if和switch的速度差不多,再分支较多的情况下,switch的速度就快多了。 这里大家可以自己测试一下。
我们知道JDK1.8之后,JDK支持String类型,是因为在变异的时候,使用hashCode来作为switch的实际值。 首先写一段switch结构的代码:
public static void main(String[] args) {
final String str = "C";
switch (str) {
case "A" :
System.out.println("A");
break;
case "B" :
System.out.println("B");
break;
default:
System.out.println("C");
}
}
public static void main(String[] args) {
String str = "C";
String var2 = "C";
byte var3 = -1;
switch(var2.hashCode()) {
case 65:
if (var2.equals("A")) {
var3 = 0;
}
break;
case 66:
if (var2.equals("B")) {
var3 = 1;
}
}
switch(var3) {
case 0:
System.out.println("A");
break;
case 1:
System.out.println("B");
break;
default:
System.out.println("C");
}
}
可以看到,switch结构中变为了String.hashcode()方法,利用其返回的int值进行判断,所以说编译后还是使用了switch(int)结构来实现的。而且我们知道,String的hashcode方法是有哈希冲突的风险的,所以我们应该在每个case条件中增加了equals作为补充判断,避免哈希冲突错误。
当存取类变量的时候,Java使用的是虚拟机指令GETFFIELD获取类变量,如果存取方法的变量,则通过出栈操作获取变量。 在之前的文章【进阶之路】攻克JVM——JVM对象及对象的访问定位(一)里有提到
JVM提出栈上分配的概念,针对作用域在方法内的对象,如果满足了逃逸分析,就会将对象属性打散后分配在栈上(线程私有的,属于栈内存),这样,随着方法的调用结束,栈空间的回收就会随着将栈上分配的打散后的对象回收掉,不再给GC增加额外的无用负担,从而提升应用程序整体的性能。
因此在需要频繁操作类变量的时候,最好先赋值给一个局部变量,比如这样:
int[] arr = new int[1024];
for (int i=0;i<1024;i++){
arr[i]=i;
}
但是在实际的开发过程中,由于CPU缓存的原因,并不是每次都从Heap(堆)中取出变量,会从CPU缓存中存取,所以在JMH测试中难以验证谁的性能更好,这边我就不展示了。但是,通过对JVM机制的学习,我们能清楚的知道局部变量的好处。
预处理指的是对于需要反复调用的代码,可以尝试取出公共的只读代码块,处理一次生成并保留处理结果。这样接下来需要反复调用的时候,可以直接引用处理的结果。
随便举个例子,假设是黑名单判断,没有做预处理的代码如下:
ServiceConfig serviceConfig =new ServiceConfig("a,A,S,DA,ASD,F,SDF,SD,F,F,A,D,F");
Set strings = serviceConfig.getblackService();
forbid =strings.contains("a");
然后稍微改一下,这里是做了预处理的
public class ServiceConfig{
String black=null;
Set blackSet =new HashSet<>();
public ServiceConfig(String black){
this.black=black;
this.blackSet.addAll(Arrays.asList(black.split(",")));
}
public Set getblackService(){
return blackSet;
}
}
ServiceConfig serviceConfig =new ServiceConfig("a,A,S,DA,ASD,F,SDF,SD,F,F,A,D,F");
Set strings = serviceConfig.getblackService();
forbid =strings.contains("a");
然后放在测试环境一跑,大家看的很明显了,谁才是版本答案:
预分配就很简单了,JDK存在大量预先分配空间的代码,比如我上一章讲的StringBuilder,会初始分配一段空间,不必每次调用append时才分配。
每次在append之前,也会检查分配空间是否足够,如果足够,则不需要增加空间。
如果所有的业务代码都能预分配合理的空间,那么系统的业务性能也会有合理的提高。
JDBC在处理SQL语句时有一个预编译的过程,而预编译对象就是把一些格式固定的SQL编译后,存放在内存池中即JDBC缓冲池,当我们再次执行相同的SQL语句时就不需要预编译的过程了,所以即使SQL注入特殊的语句,也会只当做参数传进去,不会当做指令执行。这个功能一大优势就是能提高执行速度,尤其是多次操作数据库的情况,再一个优势就是预防SQL注入,严格的说,应该是预防绝大多数的SQL注入。
还有种用法,涉及到格式化、序列化的时候,预编译成长红箭格式是一种提高性能的办法,比如在日志输出的时候可能会采用这种方法,
String类的format()方法用于创建格式化的字符串以及连接多个字符串对象,会解析format中出现的“{}”符号
(类似这样)
预编译的方式也能提升服务的性能。
我们在编写代码的过程中,稍稍一注意,就能全面提升代码的性能。这一次的系列文章也是出于这个角度所编写的,接下来我会继续的思考和查阅资料,进一步完善调优系列。 同时需要思维导图的话,可以联系我,毕竟知识越分享越香!