Java开发规范之性能篇

Java老K 2020-11-11 08:06:54
java 开发 性能 规范


JAVA开发中,大部分的性能问题原因并不在于JAVA语言本身,而是我们用这些语言写的程序,所以养成良好的编码习惯非常重要。

下面给大家分享一些日常开发中比较常见的典型案例:

一. 类中的内部方法声明为private

很多同学觉得这个无所谓,写代码时喜欢一个类里的所有方法都是public的(原因大家都懂),美其名曰:便于后期扩展。。

对于不需要外部访问的方法改为私有的,不仅仅是因为面向对象的思想,符合数据封装和安全访问原则,还有一个很大的好处就是任何private方法都是隐性的final!

Any private methods in a class are implicitly final.

《Think In Java》

摘自《Think In Java》第6章

final修饰的方法能够增加内联inline的可能性

方法内联就是把调用方函数代码"复制"到调用方函数内部,作为自己的一部分代码执行,减少因函数调用产生的开销,即JIT优化。

伪代码如下:

public int sum(int a, int b, int c, int d){
return add1(a, b) + add2(c, d);
}
private int add1(int a, int b){
return a + b;
}
private int add2(int c, int d){
return c + d;
}

内联后的代码:

public int sum(int a, int b, int c, int d){
return a + b + c + d;
}

为什么方法内联能提升性能呢?

大家都知道函数调用其实就是对栈stack的操作,即压栈和出栈过程,当一个方法被调用,一个新的栈帧会被加到栈顶,分配的本地变量和参数会存储在这个栈帧,然后跳转到目标方法代码执行,方法返回的时候,本地方法和参数被销毁,栈顶被移除,最后返回到原来的地址执行。

所以函数调用需要有一定的时间和空间开销,当一个方法体不大,但又频繁被调用时,这个时间和空间开销会相对变得很大,这样就变得非常不划算,势必会降低程序的性能。根据二八原则,80%的性能消耗其实是发生在20%的代码上,对热点代码的针对性优化可以提升整体系统的性能。

但触发方法内联是有条件的,不是说加了final修饰就可以立即触发内联,还需要根据JVM的参数:-XX:CompileThreshold判断编译次数,-XX:MaxFreqInlineSize被内联方法体的大小限制。

所以说可以使用方法内联的业务场景是:

对于频繁调用的热点方法,并且方法体不大的,建议使用final修饰(private方法会隐式地被指定final修饰符,所以无须再为其指定final修饰符)。

二. public方法建议也尽量指定final修饰符

基于上面第一条,满足上述业务场景的方法,原因同上,利于JIT优化。当然也可以直接修饰方法所在的类上,这样final的类不允许被继承,而且该类的所有方法默认都是final的。

这里补充一条限制条件:并且没有被Spring AOP代理的方法

三. 能够使用lambda表达式的地方就不要用匿名内部类实现

lambda表达式不仅仅是语法糖,它编译后的class文件在jvm中执行的指令也是有区别的,使用的指令是invokeDynamic,相比于匿名内部类的调用上开销会更小一些,因为它没有匿名内部类的初始化过程,代码上也更简洁。

匿名内部类写法:

private static final ExecutorService thredPool = Executors.newCachedThreadPool();
thredPool.submit(new Runnable() {
@Override
public void run() {
// 业务逻辑
}
});

lambda表达式写法:

private static final ExecutorService thredPool = Executors.newCachedThreadPool();
thredPool.submit(() -> {
// 业务逻辑
});

四. 尽量不要在方法中频繁调用全局变量

在类中,成员变量(全局变量)保存在堆Heap中,函数内部的局部变量(基本类型、参数、对象的引用)都保存在栈Stack中,在函数内部访问自己的这些变量肯定要比访问函数外部的变量速度要快,所以从栈中操作堆中的数据速度会比较慢。

代码示例如下(反例):

private int result = 0; // 成员变量
public void sum(int... i){
for (int j : i) {
result += j; // 频繁操作函数外部的成员变量result
}
}

修改后的代码:

private int result = 0; // 全局变量
public void sum(int... i){
int temp = 0; // 临时变量
for (int j : i) {
temp += j; // 操作函数内部的临时变量
}
result = temp; // 减少对成员变量的访问
}

五. 优化集合操作中先contains再get的写法

我们的代码中经常会遇到要判断集合中是否存在这个元素,存在再取值的业务场景,伪代码如下:

public void setOrderPrice(Order order, Map<String, Price> map){
if(map.containsKey(order.getId())){
order.setPrice(map.get(order.getId()));
} else {
order.setPrice(new Price());
}
}

其实可以直接调用get获取,然后判空,这样就省去了一次查找匹配的过程,修改后如下:

public void setOrderPrice(Order order, Map<String, Price> map){
Price price = map.get(order.getId(); // 直接调用get
if(price != null){
order.setPrice(price);
} else {
order.setPrice(new Price());
}
}

以上仅是作者这些年在Java开发性能方面的工作见解,性能优化需要在时间、效率、可读性各方面权衡,做出取舍,不要把上面的内容当成教条,活学活用,变通为宜。

文章来源:http://javakk.com/197.html

版权声明
本文为[Java老K]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000037779927

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云