深入理解java虚拟机4——前端与后端优化

5.1 前端优化(编译期优化) —— 语法糖

  • 语法糖的概念
  • 前端优化 / 语法糖
    • 泛型
    • 最常用的语法糖
      • 拆箱装箱
      • 循环遍历 for( int a : list)
      • 变长参数 public static void main( String[] args )
    • if语句优化
    • 默认构造器
    • .....

5.2 后端优化(.class文件 转 二进制机器码 / 运行期)

  • 解释器和即时编译器
  • JIT优化技术
    • 方法内联
    • 逃逸分析
    • 公共子表达式消除

5.1.1 语法糖的概念

所谓语法糖,就是java编译器把*.java编译成*.class过程中,自动生成和转换的代码


5.1.2 泛型

jdk5之后加入:java在编译后会进行 泛型擦除 的动作,即在字节码文件中,泛型信息丢失,都当作Object来处理

  • 缺点1:泛型擦除后无数的拆箱装箱导致泛型慢
  • 缺点2:运行期无法得到泛型类型信息

但是,泛型擦除只擦除Code方法表内的信息,在LocalVariableTypeTable中,这些泛型信息仍然会被保留

因此,想要获得泛型信息,可以通过反射的方式获得getGenericParameterTypes()

是否相等:true
List<Integer> integerList = new ArrayList<Integer>();
List<Short> shortList = new ArrayList<Short>();
integerList.getClass().getName() == shortList.getClass().getName();


能通过编译吗? 不能
List<? extends Number> numberList;
numberList.add(2);

泛型详解:


5.1.3 最常用语法糖

  • 自动拆箱,装修
    • java的基本类型和包装类型可以自动转换,在jdk5之后加入
    • 例如:Integet.valueOf() 和 Integer.intValue() 会把 int 和 Integer 相互转换
  • 循环遍历
    • 例如:for( int a : list)
    • 把代码还原成迭代器的实现,这也是为何遍历循环需要被遍历的类实现Iterable接口的原因
  • 变长参数
    • 例如 public static void main( String[] args )
    • 可变参数String...args在编译期间其实是一个String[] args的数组,长度会自动根据参数个数计算(如果不传参,则会传递空数组而不是null)

5.1.4 条件编译

  • 编译器会自动优化if语句
    • 例如: if ( true ){ ..... } 后面的 else / else if都不会执行
    • 例如:while ( false ){ ..... } 中的内容不会执行

5.1.5 默认构造器

  • 如果没有写构造器,会自动生成无参构造器,并在构造器内调用父类无参构造器
//自己写的版本
public class Test{
}
-------------------------------------------------------------------
//编译器生成版本
public class Test{
    //前端编译器自动加上无参构造器
    public Test{
        super();    //调用父类无参构造
    }
}

5.1.6 静态创建数组和动态创建数组


5.1.7 switch-string

  • 会把switch(string)转换成switch(hashcode),相等后再equals比较防止hash冲突;用hashcode比较效率比字符串比较高


5.1.8 switch-枚举类

会把枚举类变成int数组{1,2,3....},用int作比较


5.1.9 枚举类


5.1.10 子类重写父类方法时,返回值不一致


5.2 后端优化——即使编译器与提前编译器

5.2.1 解释器和编译器

  • 解释器:java程序最初都是解释器执行,启动快,省去编译时间,立即运行
  • 即时编译器(JIT):把越来越多的代码(热点代码)编译成本地代码,获得更高的执行效率
    • JAVA中内置了两个即使编译器,分别被称 为“客户端编译器”(Client Compiler)和“服务端编译器”(Server Compiler),或者简称为C1编译器和 C2编译器。(JDK10新增了第三个Graal目标取代C2)
    • 热点代码:被多次调用的方法;被多次执行的循环体。JIT都是把整个方法体(不是循环体)即时编译
    • 热点探测:用于探测哪些循环体或者方法应该属于热点代码(两种方式:采样或者计数器)

5.2.2 分层编译

分层编译根据编译器编译、优化的规模与耗时,划分出不同的编译层次,其中包括:


5.3 编译器优化技术

Ref:基本功 | Java即时编译器原理解析及实践 - 美团技术团队

5.3.1 方法内联

  • 最重要的优化手段
  • 如果发现热点代码并且长度不太长,会进行内联:把方法内的代码拷贝,黏贴到调用者的位置
  • 非虚方法可以直接进行内联
  • 虚方法如果只查询到一个版本,则内联(称为守护内联);如果查询到多个版本,内联缓存

5.3.2 逃逸分析

  • 最先进的优化手段
  • 分析一个对象的作用域,如果在该方法内创建的对象被外部方法引用,则称为方法逃逸;如果被其他线程引用,则称为线程逃逸
  • 如果一个对象不存在方法逃逸或者逃逸程度比较低,则可以优化:
    • 栈上分配:支持方法逃逸,不支持线程逃逸
    • 标量替换:不去创建对象,而改为创建它的若干个被这个方法使用的成员变量来代替。不支持方法逃逸和线程逃逸
    • 同步消除:如果发现不存在线程逃逸,则可以擦除同步措施

5.3.3 公共子表达式消除

int d = (c * b) * 12 + a + (a + b * c);
  • 会变成下图,因为检测到c*b = b*c
int d = b * c * 13 + 2 * a;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值