什么是逃逸分析

在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。它涉及到指针分析和形状分析。

当一个变量(或对象)在子程序中被分配时,一个指向变量的指针可能逃逸到其它执行线程中,或是返回到调用者子程序。如果使用尾递归优化(通常在函数编程语言中是需要的),对象也可以看作逃逸到被调用的子程序中。如果一种语言支持第一类型的延续性在Scheme和Standard ML of New Jersey中同样如此),部分调用栈也可能发生逃逸。

如果一个子程序分配一个对象并返回一个该对象的指针,该对象可能在程序中被访问到的地方无法确定——这样指针就成功“逃逸”了。如果指针存储在全局变量或者其它数据结构中,因为全局变量是可以在当前子程序之外访问的,此时指针也发生了逃逸。

逃逸分析确定某个指针可以存储的所有地方,以及确定能否保证指针的生命周期只在当前进程或在其它线程中。

下面我们看看Java中的逃逸分析是怎样的?

Java的逃逸分析只发在JIT的即时编译中,为什么不在前期的静态编译中就进行呢,知乎上已经有过这样的提问。逃逸分析为何不能在编译期进行?​www.zhihu.com

简单来说是可以的,但是Java的分离编译和动态加载使得前期的静态编译的逃逸分析比较困难或收益较少,所以目前Java的逃逸分析只发在JIT的即时编译中,因为收集到足够的运行数据JVM可以更好的判断对象是否发生了逃逸。关于JIT即时编译可参考JVM系列之走进JIT。

JVM判断新创建的对象是否逃逸的依据有:一、对象被赋值给堆中对象的字段和类的静态变量。二、对象被传进了不确定的代码中去运行。如果满足了以上情况的任意一种,那这个对象JVM就会判定为逃逸。对于第一种情况,因为对象被放进堆中,则其它线程就可以对其进行访问,所以对象的使用情况,编译器就无法再进行追踪。第二种情况相当于JVM在解析普通的字节码的时候,如果没有发生JIT即时编译,编译器是不能事先完整知道这段代码会对对象做什么操作。保守一点,这个时候也只能把对象是当作是逃逸来处理。下面举几个例子

public class EscapeTest {

public static Object globalVariableObject;

public Object instanceObject;

public void globalVariableEscape(){

globalVariableObject = new Object(); //静态变量,外部线程可见,发生逃逸 }

public void instanceObjectEscape(){

instanceObject = new Object(); //赋值给堆中实例字段,外部线程可见,发生逃逸 }

public Object returnObjectEscape(){

return new Object(); //返回实例,外部线程可见,发生逃逸 }

public void noEscape(){

synchronized (new Object()){

//仅创建线程可见,对象无逃逸 }

Object noEscape = new Object(); //仅创建线程可见,对象无逃逸 }

}

基于逃逸分析的优化

当判断出对象不发生逃逸时,编译器可以使用逃逸分析的结果作一些代码优化将堆分配转化为栈分配。如果某个对象在子程序中被分配,并且指向该对象的指针永远不会逃逸,该对象就可以在分配在栈上,而不是在堆上。在有垃圾收集的语言中,这种优化可以降低垃圾收集器运行的频率。

同步消除。如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步。

分离对象或标量替换。如果某个对象的访问方式不要求该对象是一个连续的内存结构,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。

对于优化一将堆分配转化为栈分配,这个优化也很好理解。下面以代码例子说明:

虚拟机配置参数:-XX:+PrintGC -Xms5M -Xmn5M -XX:+DoEscapeAnalysis

-XX:+DoEscapeAnalysis表示开启逃逸分析,JDK8是默认开启的

-XX:+PrintGC 表示打印GC信息

-Xms5M -Xmn5M 设置JVM内存大小是5M

public static void main(String[] args){

for(int i = 0; i < 5_000_000; i++){

createObject();

}

}

public static void createObject(){

new Object();

}

运行结果是没有GC。

把虚拟机参数改成 -XX:+PrintGC -Xms5M -Xmn5M -XX:-DoEscapeAnalysis。关闭逃逸分析得到结果的部分截图是,说明了进行了GC,并且次数还不少。

[GC (Allocation Failure) 4096K->504K(5632K), 0.0012864 secs]

[GC (Allocation Failure) 4600K->456K(5632K), 0.0008329 secs]

[GC (Allocation Failure) 4552K->424K(5632K), 0.0006392 secs]

[GC (Allocation Failure) 4520K->440K(5632K), 0.0007061 secs]

[GC (Allocation Failure) 4536K->456K(5632K), 0.0009787 secs]

[GC (Allocation Failure) 4552K->440K(5632K), 0.0007206 secs]

[GC (Allocation Failure) 4536K->520K(5632K), 0.0009295 secs]

[GC (Allocation Failure) 4616K->512K(4608K), 0.0005874 secs]

这说明了JVM在逃逸分析之后,将对象分配在了方法createObject()方法栈上。方法栈上的对象在方法执行完之后,栈桢弹出,对象就会自动回收。这样的话就不需要等内存满时再触发内存回收。这样的好处是程序内存回收效率高,并且GC频率也会减少,程序的性能就提高了。

优化二 同步锁消除

如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步。

虚拟机配置参数:-XX:+PrintGC -Xms500M -Xmn500M -XX:+DoEscapeAnalysis。配置500M是保证不触发GC。

public static void main(String[] args){

long start = System.currentTimeMillis();

for(int i = 0; i < 5_000_000; i++){

createObject();

}

System.out.println("cost = " + (System.currentTimeMillis() - start) + "ms");

}

public static void createObject(){

synchronized (new Object()){

}

}

运行结果

cost = 6ms

把逃逸分析关掉:-XX:+PrintGC -Xms500M -Xmn500M -XX:-DoEscapeAnalysis

运行结果

cost = 270ms

说明了逃逸分析把锁消除了,并在性能上得到了很大的提升。这里说明一下Java的逃逸分析是方法级别的,因为JIT的即时编译是方法级别。

优点三 分离对象或标量替换。

这个简单来说就是把对象分解成一个个基本类型,并且内存分配不再是分配在堆上,而是分配在栈上。这样的好处有,一、减少内存使用,因为不用生成对象头。 二、程序内存回收效率高,并且GC频率也会减少,总的来说和上面优点一的效果差不多。

OK,现在我们又知道了一件聪明的JVM在背后为我们做的事了。

java 逃逸分析_JVM之逃逸分析相关推荐

  1. java mat类型_JVM MAT使用分析详解

    MAT简介 MAT是一款非常强大的内存分析工具,在Eclipse中有相应的插件,同时也有单独的安装包.在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当 ...

  2. java annotiace原理_JVM源码分析之javaagent原理完全解读

    概述 本文重点讲述javaagent的具体实现,因为它面向的是我们java程序员,而且agent都是用java编写的,不需要太多的c/c++编程基础,不过这篇文章里也会讲到JVMTIAgent(c实现 ...

  3. 3704对象关闭时_JVM 通过逃逸分析就能让对象在栈上分配?没那么简单!

    本文转载自公众号 星哥笔记 作者:Danny姜 校对:承香墨影 经常会有面试官会问一个问题:Java 中的对象都是在"堆"中创建吗? 然后跟求职者大谈特谈「逃逸分析」,说通过「逃逸 ...

  4. java中Mark接口_JVM源码分析之Java对象头实现

    原标题:JVM源码分析之Java对象头实现 原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 "365篇原创计划"第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Ja ...

  5. 描述一下JAVA的加载过程_JVM源码分析之Java类的加载过程

    简书 占小狼 转载请注明原创出处,谢谢! 趁着年轻,多学习 背景 最近对Java细节的底层实现比较感兴趣,比如Java类文件是如何加载到虚拟机的,类对象和方法是以什么数据结构存在于虚拟机中?虚方法.实 ...

  6. 死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  7. java dump分析工具_java性能分析与常用工具

    本次源码已放在Github:https://github.com/nateshao/jvm-tuning 个人博客 https://nateshao.gitee.io http://www.nates ...

  8. java 计算移动平均线_基于Java语言开发的个性化股票分析技术:移动平均线(MA)...

    基于Java语言开发的个性化股票分析技术:移动平均线(MA) 基于 Java 语言开发的个性化股票分析技术:移动平均线(MA)移动平均线(MA)是以道·琼斯的"平均成本概念"为理论 ...

  9. Java线程池ThreadPoolExecutor使用和分析

    Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) Java线程池ThreadPoolExecutor使用和分析(三 ...

  10. linux hashmap,Java中对HashMap的深度分析与比较

    Java中对HashMap的深度分析与比较 在Java的世界里,无论类还是各种数据,其结构的处理是整个程序的逻辑以及性能的关键.由于本人接触了一个有关性能与逻辑同时并存的问题,于是就开始研究这方面的问 ...

最新文章

  1. 基于K8S构建企业级Jenkins CI/CD平台实战(一) 之 环境搭建
  2. 力扣(leetcode)-1. 两数之和
  3. linux thrift 安装目录,Linux下安装Thrift
  4. oracle更新数据没有变化,往oracle里面写入数据,怎么表空间都没有变化
  5. php连接mysql开发环境_PHP开发环境搭建及常用的数据库操作
  6. python常用内置方法_Python3 常用的几个内置方法
  7. 飞书上线“程序员友好”功能 迎接1024程序员节
  8. 大型翻车现场?人人车官博辟谣破产传闻 却被群嘲官博怕是还蒙在鼓里
  9. AD域首次登陆修改密码设置
  10. Python 彻底甩掉 Java,位居 48 种编程语言之首!
  11. htc+one+m7+linux驱动,HTC One M7简易刷Recovery教程
  12. java复制文件拒绝访问权限_关于IO流在复制文件时出现java.io.FileNotFoundException: D:\xxx (拒绝访问。) 拒绝访问的问题...
  13. mvp架构 java_MVP架构基本使用
  14. 标准差(standard deviation)
  15. 2021年的放假安排正式官宣!五一连休5天哦!
  16. 认识计算机拓扑结构图,认识计算机网络拓扑结构
  17. Linux link命令
  18. Windows上部署Discuz论坛
  19. CoffeeScript笔记
  20. 接口中的变量public static final

热门文章

  1. 马哥教育N38第二周作业
  2. Akka 指南 之「集群分片」
  3. SLAM中的边缘化舒尔补
  4. [YYOJ]LZY喜欢的数字
  5. 短信中心号码iphone_如何在iPhone上阻止来自特定号码的呼叫
  6. Shiro的Subject对象详解
  7. PDO 学习与使用 ( 二 ) PDO 数据提取 和 预处理语句
  8. 通向大规模医疗知识图谱:万字详解天衍实验室知识图谱对齐技术
  9. day4-反爬和正则
  10. Intel Edison 基础开发之配置第一个小程序