博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
运行期优化
阅读量:5991 次
发布时间:2019-06-20

本文共 2088 字,大约阅读时间需要 6 分钟。

一、概述

即时编译器(Just In Time Compiler,JIT编译器):Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行

特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关
的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器。

二、HotSpot虚拟机内的即时编译器

1、解释器与编译器

当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译时间立即执行。

在程序运行后,随时间推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以提供执行效率。

HotSpot虚拟机中内置两个即时编译器,分别称为Client Compiler 和Server Compiler,简称C1编译器和C2编译器。

默认C1编译器,可以使用-client 或 -server 指定。
解释器与编译器搭配使用的方式在虚拟机中称为混合模式,用户可以通过参数-Xint强制虚拟机运行与解释模式(Interpreted Mode),
这时编译器完全不参与工作,全部代码都使用解释方式执行。
也可以使用-Xcomp强制虚拟机运行编译模式(Compiled Mode),这时将优先采用编译方式执行程序。
Client Compiler 可以获得更高的编译速度,Server Compiler可以获得更好的编译质量。

2、编译对象与触发条件u

热点代码:

  • 被多次调用的方法
  • 被多次执行的循环体

    判断一段代码是不是热点代码,是不是需要触发即时编译,这样的行为称为热点探测。热点探测方式:

  • 基于采样的热点探测:虚拟机周期性的检查各个线程的栈顶,如果发现某个方法经常出现在栈顶,那这个方法就是热点方法。
  • 基于计数器的热点探测:虚拟机为每个方法建立计数器,统计方法的执行次数,如果执行次数超过一定阈值,就认为是热点方法。
    HotSpot采样的是基于计数器的热点探测,为每个方法准备两类计数器:方法调用计数器、回边计数器。

方法调用计数器:统计方法被调用的次数,Client模式下默认1500次,Server模式下默认10000次,可以通过虚拟机参数-XX:CompiledThreshold修改。

回边计数器:统计一个方法中循环体代码执行次数。

三、编译优化技术

1、公共子表达式消除

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

对于这种表达式就没有必要花时间对它进行计算,只需直接用前面计算过的表达式结果代替E就可以了。

int d = (c * b) * 12 + a + (a + b * c);        int d = E * 12 + a + (a + E);

当这段代码进入虚拟机即时编译器编译后,就变为了第二行代码

2、数组边界检查消除

java 在访问数组类型数据时会自动进行上下界范围检查,如果超出范围将抛出ArrayIndexOutOfBoundsException,每次数据读写都带有一次

隐含的条件判定,影响性能。对于循环体来说,如果编译器可以判定循环变量取值范围永远在[0,length())之间,那在整个循环中就可以
把数组上下界检查消除。

3、方法内联

把目标的代码复制到发起调用的方法中,避免发生真实的方法调用。

分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸。
甚至可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。
如果能证明一个对象不会逃逸到变量或线程外,也就是别的方法或线程无法通过任何途径访问到这个对象,则可能为为这个变量进行优化。

  • 栈上分配:java虚拟机中,在java堆上分配创建对象的内存空间,java堆中的对象对于各个线程都是共享可见的,只要持有这个对象的引用,
    就可以访问堆中存储的对象数据。虚拟机垃圾收集系统可以回收堆中不再使用的对象。

如果确定一个对象不会逃逸出方法之外,那让这个对象在栈上分配,对象占用的内存就可以随栈帧出栈而销毁。

  • 同步消除:线程同步本身就是一个相对耗时的过程,如果逃逸分析能够确定一个变量不会逃逸出线程,无法被其他线程访问,那这个变量的读写
    肯定就不会有竞争,对这个变身实施同步措施也就可以消除。
  • 标量替换:标量是指一个数据已经无法分解成更小的数据来表示,Java虚拟机中的原始数据类型都不能再进一步分解,就可以称为标量。
    相对的,如果一个数据可以继续分解,称为聚合量,java中的对象就是典型的聚合量。如果把一个Java对象拆散,根据程序访问的情况,将其使用

到的成员变量恢复原始类型来访问就叫标量替换。如果逃逸分析证明一个对象不会被外部访问,并且对象可以被拆散的话, 程序真正执行的时候

可能不创建这个对象,而改为直接创建它的若干被这个方法使用到的成员变量替换。这些变量建立在栈上分配读写。

转载地址:http://cqnlx.baihongyu.com/

你可能感兴趣的文章
树和二叉树简介
查看>>
左手读红楼梦,右手写 BUG,闲快活
查看>>
Python猫荐书系列之五:Python高性能编程
查看>>
CSS原理解析之模型篇
查看>>
如何在自定义 Tool Bar 和 Tab Bar 之间切换显示
查看>>
云技术平台赋能媒体融合发展创新
查看>>
流量和广告
查看>>
JavaScript EventEmitter
查看>>
Python 从入门到入门基础练习十五题
查看>>
Redux源码分析--中间件篇
查看>>
Java 基础(十)字节流
查看>>
回首2018,展望2019 | 掘金年度征文
查看>>
JavaScript数组小方法
查看>>
Java&Android 基础知识梳理(1) 注解
查看>>
2018,很多APP的logo渐变了 -- 渐变色教程
查看>>
Flutter中的3D透视效果
查看>>
5月,请对开发者好一点。
查看>>
阿里云上Kubernetes集群联邦
查看>>
iOS 高德地图自定义气泡处理
查看>>
聊聊hystrix的execution.isolation.semaphore.maxConcurrentRequests属性
查看>>