java优化知识

final的合理使用

final修饰的 不能被派生;方法不能被重写;变量不能被修改。

基于这个特性,java会事先找出内联的所有方法,提高运行效率

重用对象

性能消耗:对象的生成 和 回收

对于多个字符串的连接时尽量使用StringBuffer/StringBuilder

少使用全局变量

方法体内的局部变量及方法传递的参数会在栈中临时存储,方法调用结束后就会自动消除,不需要额外的回收消耗.

及时关闭流

系统的I/O操作会很消耗系统性能,及时关闭会避免一些性能及不可预期的错误.

减少对变量的重复计算/列表的重复遍历

可重用的计算结果,与可在同一次遍历得出的结果可以合并在一起.

如 一些排序(一般是内置方法,可以自行定义)与循环取值计算的操作可以合并

尽量采用懒加载的方法

在一些程序中会有不执行的语句,这些语句所使用的变量应该在语句内创建

Object o = new Object("eg");
if(exist){
    String a = o.getName();
}else{
    a = "";
}

//换成
Object o = new null;
if(exist){
    o = new Object("eg");
    String a = o.getName();
}else{
    a = "";
}
慎用异常

异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。

异常只能用于错误处理,不应该用来控制程序流程。

可能的话指定集合/StringBuilder的长度

集合的初始长度往往不能满足业务需求,而自动扩容又会消耗性能.

如果能够估计到集合的合理大小并设置初始大小,就可以避免集合的自动扩容所带来的消耗.

大量数据复制时,使用System.arraycopy

System.arraycopy:一维数组属性->值复制;一维数组对象->引用复制;二维数组->引用复制.

System.arraycopy是对内存直接进行复制,减少了for循环过程中的寻址时间,从而提高了效能(针对于深拷贝方式)

乘法和除法作用移位

在计算机底层对位的操作是最快捷的,不需要对乘法或除法进行解析。

注意:java编译器会对*和%运算运行自动优化,不需要我们去考虑。而对于除法,在保证被除数不对负的情况下应该使用位移运算。

int val = 10;
//int a = val * 8;
int b = val / 4;

//改进为
int val = 10;
//int a = val << 3;
int b = val >> 2;
集合的同步与异步选择使用

同步用HashMap,ArrayList,StringBuilder;异步用ConcurrentHashMap,Vector,StringBuffer.

单例的合理使用

基于对象的高复用。

spring的依赖注入模式即可看成是统一管理单例

集合中实现RandomAccess接口的类如ArrayList的随机访问(for)与顺序访问(foreach)

实现了RandomAcecss接口的集合在随机访问时应使用for,顺序访问时应使用foreach。foreach使用iterator进行遍历。

同步块代替同步方法

作用数据连接池和线程池

节省创建连接和线程创建开销

使用带缓冲的输入输出流进行I/O操作

带缓冲的输入输出流,即BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,这可以极大地提升IO效率

集合特性与作用场景的明确

字符串变量和字符串常量equals的时候将字符串常量写在前面

避免出现空指针异常

String a = "aaa";
if(a.equals("aaa"))
    //do something
    
//改为
String a = "aaa";
if("aaa".equals)
    //do something
基本数据类型转String各方法效率

String.valueOf()在底层会调用Integer.toString()但会做一个空判断。+操作符会先用append进行拼接,再用toString().

i.toString() > String.valueOf(i) > i + “”

Map的值遍历与键遍历

Map.entrySet 进行值及键的遍历;使用Map.keySet()/values()进行键或值的遍历。

for(Map.Entry e : map.entrySet){
    e.getKey();
    e.getValue();
}
资源的close()分开catch

避免一个资源异常导致另一个正常资源不会被回收

对于ThreadLocal使用前或者使用后一定要先remove

对线程池/Session的重用会使一个线程使用多次,所以其local变量应该清理(会话清理)。

用常量或枚举替代魔鬼数据

可读性和可编辑性增加

在循环内部不要进行+的字符串拼接

java编辑器不会对循环内的+字符串拼接进行优化。反而会不停地创建StringBuilder进行append(浪费内存)。


编译优化
  • 语言无关的经典优化技术之一:公共子表达式消除>>>
  • 语言无关的经典优化技术之一:数组范围检查消除>>>
  • 最重要的优化技术之一:方法内联>>>
  • 最前沿的优化技术之一:逃逸分析>>>
1. 公共子表达式消除

如果一个表达式E已经计算过了,并且从先前的计算到现在E中的所有变量值都没有发生变化,那么E的这次出现就成为了公共子表达式

eg: x = b * c + c * b;(如在这两个表达式之间c与b没有变化)则b * c就会被当作一个表达式E:

x = 2 * E;

2. 数组范围检查消除

java会对数组的访问进行越界判定,优化时jvm会对数组的数据流分析判定是否越界,以节省判定消耗。

3. 方法内联

java会把final方法调用使用内联的方式把被调用方的代码加入调用方里(由于多态的存在),以便节省方法寻找的消耗。

所以加为final的方法会被及时的找到并内联进方法会内。

4. 逃逸分析

jvm中为其他优化手段提供了分析技术。

逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中去,称为方法逃逸。甚至可能被外部线程访问到,比如赋值给类变量或可以在其他线程中访问到的实例变量,称为线程逃逸。

如果能证明一个对象不会逃移到方法外或者线程之外,也就是别的方法或线程无法通过任何途径访问到这个对象,则可能为这个变量进行一些高效的优化:

  1. 栈上分配

    Java虚拟机中,对象在堆上分配这个众所周知。虚拟机的垃圾收集系统可以回收堆中不再使用的对象,但回收动作无论是筛选可回收对象还是回收和整理内存都要耗费时间。
    如果确定一个对象不会逃逸出方法之外,那么让这个对象在栈上分配将会是一个不错的主意,对象所占用的内存空间就可以随着栈帧出栈而销毁,这样垃圾收集系统的压力将会小很多
  2. 同步消除

    如果确定一个对象不会逃逸出方法之外,那么使用在该对象上的同步措施就可以被消除
  3. 标题替换

标量:数据无法再次细分(元子数据,如基本数据类型)。对象则是可细分的,是聚合量

如果确定一个对象不会逃逸出方法之外,则可以不创建对象,进而创建若干被这个方法使用的对象的成员变量。这些成员变量除了会被分配进栈里外,还会为后续优化作基础。

如果有需要,并且确认对程序运行有益,可以使用参数:
-XX:+DoEscapeAnalysis
来手动开启逃逸分析,开启之后可以通过参数:
-XX:+PrintEscapeAnalysis
来查看分析结果。有了逃逸分析支持之后,就可以使用参数:
-XX:+EliminateAllocations
来开启标量替换,使用参数:
-XX:+EliminatLocks
来开启同步消除,使用参数:
-XX:+PrintEliminateAllocations
查看标量的替换情况。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值