9.运行期优化

运行期优化

本章讲述针对《008.编译期优化.md》中第二类编译过程的优化。

1. 概述

Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法、代码块的运行特别频繁时,就会把这些代码认定为"热点代码"。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(JIT、Just In Time)。

2. HotSpot的即时编译器

2.1 解释器与编译器

解释器可以作为编译器激进优化的一个逃生门,让编译器根据概率选择一些大多数时候都能提升运行速度的优化手段,当激进优化的假设不成立,如加载了新类后类型继承结构出现变化、出现"罕见陷阱"时可以通过逆优化退回到解释状态继续执行。

HotSpot内置了两个即时编译器:Client Compiler(C1),Server Compiler(C2)。

解释器、编译器搭配模式:

  1. 混合模式(mixed mode)

    解释器、编译器搭配使用。-client使用C1编译器,-server使用C2编译器。

  2. 解释模式(interpreted mode)

    只有解释器工作,使用-Xint指定。

  3. 编译模式(compiled mode)

    优先采用编译方式执行程序,但是解释器仍要在编译无法进行情况下介入执行过程。使用-Xcomp指定。

解释器与编译器共同使用时,想要编译出优化程度更高的代码,解释器可能还要替编译器收集性能监控信息,这对解释器执行速度有影响。为了达到平衡,采用分层编译:

  1. 第0层,程序解释执行,解释器不开启性能监控工具(Profiling),可触发第1层编译。
  2. 第1层,也称为C1编译,将字节码编译为本地代码,进行简单、可靠的优化,如有必要将加入性能监控的逻辑。
  3. 第2层,也称为C2编译,将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

实施分层编译后,Client Compiler、Server Compiler将会同时工作,许多代码都可能会被多次编译,用Client Compiler获取更高的编译速度,用Server Compiler获取更好的编译质量,在解释执行时也无需再承担收集性能监控信息的任务。

2.2 编译对象与触发条件

热点代码分为两类:

  1. 被多次调用的方法

    编译器会以整个方法为编译对象,是虚拟机中标准的JIT编译方式。

  2. 被多次执行的循环体

    尽管编译动作是循环体触发,但编译器仍以整个方法(而不是单独的循环体)作为编译对象。这种编译方式因为编译发生在方法执行过程中,因此称为栈上替换(OSR编译,即方法栈帧还在栈上,方法被替换了。)

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

  1. 基于采样的热点探测

    周期性地检查各个线程的栈顶,如果发现某个方法经常出现在栈顶,就是热点方法。

    容易受到线程阻塞或别的外界因素影响而扰乱热点探测。

  2. 基于计数器的热点探测

    为每个方法建立计数器,统计方法的执行次数。如果执行次数超过一定的阈值就认定为热点方法。

    HotSpot采用这种方式。

    方法调用计数器、回边计数器(统计方法中循环体代码执行的次数)

    如果不做任何设置,方法调用计数器只是统计一段时间内的次数,当超过时间后,会进行热度衰减。

3. 编译优化技术

只介绍几种编译优化技术

3.1 公共子表达式消除

如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就称为了公共子表达式。没有必要花时间再对它进行计算,只需要直接用前面计算过的表达式结果代替E就可以了。

3.2 数组边界检查消除

3.3 方法内联

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

3.4 逃逸分析

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

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

  1. 栈上分配

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

  2. 同步消除

    如果逃逸分析确定一个变量不会逃逸出线程,无法被其他线程访问,那这个变量的读写就不会有竞争,对这个变量实施的同步措施就可以消除掉。

  3. 标量替换

    标量是指一个数据已经无法再分解为更小的数据来表示了。

    Java虚拟机中的原始数据类型(int、long等)都不能再进一步分解,他们可以称为标量。

    如果一个数据可以继续分解,它就称为聚合量。Java中的对象就是最典型的聚合量。

    如果把一个Java对象拆散,根据程序访问的情况,将其使用到的成员变量恢复原始类型来访问就叫做标量替换。

    如果逃逸分析证明一个对象不会被外部访问,并且这个对象可以被拆散的话,那程序真正执行时,可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。

    将对象拆分后,除了可以让对象的成员变量在栈上分配和读写外,还可以为后续进一步优化手段创造条件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容