浅谈HotSpot逃逸分析

JIT

即时编译(Just-in-time Compilation,JIT)是一种通过在运行时将字节码翻译为机器码,从而改善字节码编译语言性能的技术。在HotSpot实现中有多种选择:C1、C2和C1+C2,分别对应client、server和分层编译。
1、C1编译速度快,优化方式比较保守;
2、C2编译速度慢,优化方式比较激进;
3、C1+C2在开始阶段采用C1编译,当代码运行到一定热度之后采用G2重新编译;
在1.8之前,分层编译默认是关闭的,可以添加-server -XX:+TieredCompilation参数进行开启。

逃逸分析

逃逸分析并不是直接的优化手段,而是一个代码分析,通过动态分析对象的作用域,为其它优化手段如栈上分配、标量替换和同步消除等提供依据,发生逃逸行为的情况有两种:方法逃逸和线程逃逸。
1、方法逃逸:当一个对象在方法中定义之后,作为参数传递到其它方法中;
2、线程逃逸:如类变量或实例变量,可能被其它线程访问到;

如果不存在逃逸行为,则可以对该对象进行如下优化:同步消除、标量替换和栈上分配。

同步消除

线程同步本身比较耗,如果确定一个对象不会逃逸出线程,无法被其它线程访问到,那该对象的读写就不会存在竞争,则可以消除对该对象的同步锁,通过-XX:+EliminateLocks可以开启同步消除。

标量替换

1、标量是指不可分割的量,如java中基本数据类型和reference类型,相对的一个数据可以继续分解,称为聚合量;
2、如果把一个对象拆散,将其成员变量恢复到基本类型来访问就叫做标量替换;
3、如果逃逸分析发现一个对象不会被外部访问,并且该对象可以被拆散,那么经过优化之后,并不直接生成该对象,而是在栈上创建若干个成员变量;
通过-XX:+EliminateAllocations可以开启标量替换, -XX:+PrintEliminateAllocations查看标量替换情况。

栈上分配

故名思议就是在栈上分配对象,其实目前Hotspot并没有实现真正意义上的栈上分配,实际上是标量替换。

 

  private static int fn(int age) {User user = new User(age);int i = user.getAge();return i;}

User对象的作用域局限在方法fn中,可以使用标量替换的优化手段在栈上分配对象的成员变量,这样就不会生成User对象,大大减轻GC的压力,下面通过例子看看逃逸分析的影响。

 

public class JVM {public static void main(String[] args) throws Exception {int sum = 0;int count = 1000000;//warm upfor (int i = 0; i < count ; i++) {sum += fn(i);}Thread.sleep(500);for (int i = 0; i < count ; i++) {sum += fn(i);}System.out.println(sum);System.in.read();}private static int fn(int age) {User user = new User(age);int i = user.getAge();return i;}
}class User {private final int age;public User(int age) {this.age = age;}public int getAge() {return age;}
}

分层编译和逃逸分析在1.8中是默认是开启的,例子中fn方法被执行了200w次,按理说应该在Java堆生成200w个User对象。

1、通过java -cp . -Xmx3G -Xmn2G -server -XX:-DoEscapeAnalysis JVM运行代码,-XX:-DoEscapeAnalysis关闭逃逸分析,通过jps查看java进程的PID,接着通过jmap -histo [pid]查看java堆上的对象分布情况,结果如下:


可以发现:关闭逃逸分析之后,User对象一个不少的都在堆上进行分配。

 

2、通过java -cp . -Xmx3G -Xmn2G -server JVM运行代码,结果如下:


可以发现:开启逃逸分析之后,只有41w左右的User对象在Java堆上分配,其余的对象已经通过标量替换优化了。

 

3、通过java -cp . -Xmx3G -Xmn2G -server -XX:-TieredCompilation运行代码,关闭分层编译,结果如下:


可以发现:关闭了分层编译之后,在Java堆上分配的User对象降低到1w多个,分层编译对逃逸分析还是有影响的。

 

编译阈值

即时编译JIT只在代码段执行足够次数才会进行优化,在执行过程中不断收集各种数据,作为优化的决策,所以在优化完成之前,例子中的User对象还是在堆上进行分配。

那么一段代码需要执行多少次才会触发JIT优化呢?通常这个值由-XX:CompileThreshold参数进行设置:
1、使用client编译器时,默认为1500;
2、使用server编译器时,默认为10000;
意味着如果方法调用次数或循环次数达到这个阈值就会触发标准编译,更改CompileThreshold标志的值,将使编译器提早(或延迟)编译。

除了标准编译,还有一个叫做OSR(On Stack Replacement)栈上替换的编译,如上述例子中的main方法,只执行一次,远远达不到阈值,但是方法体中执行了多次循环,OSR编译就是只编译该循环代码,然后将其替换,下次循环时就执行编译好的代码,不过触发OSR编译也需要一个阈值,可以通过以下公式得到。

 

-XX:CompileThreshold = 10000 
-XX:OnStackReplacePercentage = 140
-XX:InterpreterProfilePercentage = 33
OSR trigger = (CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100 = 10700

其中trigger即为OSR编译的阈值。

那么如果把CompileThreshold设置适当小一点,是不是可以提早触发编译行为,减少在堆上生成User对象?我们可以进行通过不同参数验证一下:
1、-XX:CompileThreshold = 5000,结果如下:

2、-XX:CompileThreshold = 2500,结果如下:

 


3、-XX:CompileThreshold = 2000,结果如下:

 

4、-XX:CompileThreshold = 1500,结果如下:

 

在我的机器中,当设置到1500时,在堆上生成的User对象反而升到4w个,目前还不清楚原因是啥...

JIT编译在默认情况是异步进行的,当触发某方法或某代码块的优化时,先将其放入编译队列,然后由编译线程进行编译,编译之后的代码放在CodeCache中,CodeCache的大小也是有限的,通过-XX:-BackgroundCompilation参数可以关闭异步编译,我们可以通过执行java -cp . -Xmx3G -Xmn2G -server -XX:CompileThreshold=1 -XX:-TieredCompilation -XX:-BackgroundCompilation JVM命令看看同步编译的效果:在java堆上只生成了2个对象。

当然了,这是为了好玩而进行的测试,生产环境不要随意修改这些参数:
1、热点代码的编译过程是有成本的,如果逻辑复杂,编程成本更高;
2、编译后的代码会被存放在有大小限制的CodeCache中,如果CompileThreshold设置的太低,JIT会将一大堆执行不那么频繁的代码进行编译,并放入CodeCache,导致之后真正执行频繁的代码没有足够的空间存放;



链接:https://www.jianshu.com/p/20bd2e9b1f03
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/576531.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pku 1611 The Suspects 并查集的应用

http://poj.org/problem?id1611 思路&#xff1a;统计出和0能够联系在一起的点&#xff0c;然后输出其个数 View Code #include <cstdio>#include <iostream>#define maxn 30004using namespace std;int f[maxn],num[maxn];//num记录与0有联系的个数int n,m;int …

Java陷阱(一)——ArrayList.asList

一、问题代码 话不多说&#xff0c;直接上问题代码&#xff1a; package com.pajk.recsys.dk.test;import java.util.ArrayList; import java.util.Arrays; import java.util.List;import com.pajk.recsys.utils.CommonUtils;public class CommonTest {public static List<…

ElasticSearch bool过滤查询

bool过滤查询 可以实现组合过滤查询 格式: { "bool": { "must: 0, "should: O, "must not":0}} must:必须满足的条件---and should:可以满足也可以不满足的条件--or must_ not:不需要满足的条件--not GET /lib5/items/_search { "post_fi…

Word2Vec训练同义词模型

一、需求描述 业务需求的目标是识别出目标词汇的同义词和相关词汇&#xff0c;如下为部分目标词汇(主要用于医疗问诊)&#xff1a; 尿 痘痘 发冷 呼吸困难 恶心 数据源是若干im数据&#xff0c;那么这里我们选择google 的word2vec模型来训练同义词和相关词。 二、数据处理…

ElasticSearch 聚合查询

价格总和&#xff1a; 1,使用aggs 2,自己起个名字price_of_sum 3,求和sum 4&#xff0c;filed要求和的字段 GET /lib5/items/_search {"aggs": {"price_of_sum": {"sum": {"field": "price"}}} }聚合查询结果&#x…

hdu Candy Sharing Game

http://acm.hdu.edu.cn/showproblem.php?pid1034 模拟题 View Code 1 #include<iostream> 2 using namespace std; 3 int a[100000]; 4 int b[100000];//a的一半 5 int n; 6 int main() 7 { 8 while(cin>>n && n) 9 {10 int i;11 …

Spark下的word2vec模型训练

一、引言 前边一节介绍了Word2Vec模型训练同义词&#xff0c;那么在大数据量的情况下&#xff0c;我们自然想到了用spark来进行训练。下面就介绍我们是如何实现spark上的模型训练。 二、分词 模型训练的输入是分好词的语料&#xff0c;那么就得实现spark上的分词。 def split…

ElasticSearch 复合查询

使用bool查询 接收以下参数: must:文档必须匹配这些条件才能被包含进来。 must _not:文档必须不匹配这些条件才能被包含进来。 should:如果满足这些语句中的任意语句&#xff0c;将增加。score, 否则&#xff0c;无任何影响。它们主要用于修正每个文档的相关性得分。 filter:必…

对DataTable的一些解释

最经做导入导出Excel时&#xff0c;经常操作DataTable&#xff0c;DataSet&#xff0c;就按自己的需求对他们的一些常用的东西进行了一下总结。 一、DataTable简介 (1)构造函数 DataTable() 不带参数初始化DataTable 类的新实例。 DataTable(string …

最优化学习笔记(二十)——全局搜索算法

一、引言 前边的博文我们讨论过一些迭代算法&#xff0c;包括梯度方法、牛顿法、共轭梯度法和拟牛顿法&#xff0c;能够从初始点出发&#xff0c;产生一个迭代序列&#xff0c;但是往往这些迭代序列只能收敛到局部极小点&#xff0c;而且这些迭代方法需要计算目标函数的一阶导数…

Elasticsearch查询性能优化

constant_score的用处 当我们不关心检索词频率TF&#xff08;Term Frequency&#xff09;对搜索结果排序的影响时&#xff0c;可以使用constant_score将查询语句query或者过滤语句filter包装起来。 检索词频率&#xff1a;检索词在该字段出现的频率。出现频率越高&#xff0c…

Doc2Vec训练相似文章识别模型

一、需求描述 由于在平台上发布的文章是来源于不同渠道和作者的&#xff0c;发布的文章也存在一定程度上的抄袭或者太相似。为了避免对用户体验造成影响&#xff0c;避免不必要的文章费用支出&#xff0c;需要识别出哪些文章是相似的。数据源是若干文章&#xff08;中文&#x…

微软ping程序源代码完整版(附详细的注释)

作者:侯志江 单位:天津大学软件学院 E-mail :tjuhzjemailyahoo.com.cn 日期:2005年1月1日 内容简介: 编写自己的一个ping程序,可以说是许多人迈出网络编程的第一步吧!!这个ping程序的源代码经过我的修改和调试,基本上可以取代windows中自带的ping程序. 各个模块…

ElasticSearch原理

3.1解析es的分布式架构 3.1.1分布式架构的透明隐藏特性 ElasticSearch是一个分布式系统&#xff0c; 隐藏了复杂的处理机制 分片机制:我们不用关心数据是按照什么机制分片的、最后放入到哪个分片中 分片的副本: 集群发现机制(cluster discovery):比如当前我们启动了一-个es进程…

实时重复文章识别——SimHash

一、背景介绍 在前边的文章中&#xff0c;我们采用的是用google的Doc2Vec模型来识别重复文章的&#xff0c;从线上运行的效果来看&#xff0c;它的准确率是比较高的。当然&#xff0c;这是建立在把所有的文章都当做训练数据来训练Doc2Vec模型的基础上的&#xff0c;它推断出一篇…

Duplicate entry...for key...

Duplicate entry...for key...的错误原因是主键的唯一值重复&#xff0c;在对数据库进行修改、插入操作时&#xff0c;一但主键的唯一值重复就会报此错误&#xff0c;有时在表中存在多个主键时&#xff0c;对表操作仍然报此错误&#xff0c;是因为对该表的索引造成的。例如一个…

深入理解simhash原理

一、LSH 介绍 LSH(Locality sensitive hashing)是局部敏感性hashing&#xff0c;它与传统的hash是不同的。传统hash的目的是希望得到O&#xff08;1&#xff09;的查找性能&#xff0c;将原始数据映射到相应的桶内。 LSH的基本思想是将空间中原始数据相邻的2个数据点通过映…

我的bolg,我的收获(MVC首次接触)

一&#xff1a;VO&#xff1a;定义变量和getter和getter方法。 二&#xff1a;DBC&#xff1a;DateBaseConnection 三&#xff1a;接口&#xff1a;记住接口首字母用I开头。并且查询时用FindXxx等等区别 四:Impl实现类&#xff1a; 五&#xff1a;代理模式&#xff0c;负责关闭…

IntelliJ IDEA tomcat配置

1&#xff0c;首先安装好 IntelliJ IDEA 开发工具 2&#xff0c;右上角这里有个 Edit Configurations 3,添加模板 选择本地安装的tomcat 和 选择jdk 4&#xff0c;添加tomcat 这里tomcat就安装好了

Think in Java之斐波那契数列

斐波纳契数列&#xff08;Fibonacci Sequence&#xff09;&#xff0c;又称黄金分割数列。 指的是这样一个数列&#xff1a;1、1、2、3、5、8、13、21、……这个数列从第三项开始&#xff0c;每一项都等于前两项之和。 在数学上&#xff0c;斐波纳契数列以如下被以递归的方法定…