一  什么是逃逸  

  逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到;这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量引用。

  正常的方法调用中,方法体中创建的对象将在执行完毕之后,垃圾回收器将回收其中创建的对象;故由于无法回收,即成为逃逸。

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

  方法逃逸的几种方式如下:

public class EscapeTest {
  public static Object obj;
  public void globalVariableEscape() { // 给全局变量赋值,发生逃逸
    obj = new Object();
  }
  public static StringBuffer craeteStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb; //方法返回值,发生逃逸
  }
  public void instanceEscape() { // 实例引用发生逃逸
    test(this);
  }
}

  如果开启逃逸分析,那么即时编译器(Just-in-time Compilation,JIT)就可以对代码做如下优化:

  (1)同步省略(锁消除):如果确定一个对象不会逃逸出线程,即对象被发现只能被一个线程访问到,无法被其它线程访问到,那该对象的读写就不会存在竞争,对这个变量的同步措施就可以消除掉。

  (2)将堆分配转化为栈分配:栈上分配就是把方法中的变量和对象分配到栈上,方法执行完后栈自动销毁,而不需要垃圾回收的介入,从而提高系统性能。。

  (3)分离对象或标量替换。Java虚拟机中的原始数据类型(int,long等数值类型以及reference类型等)都不能再进一步分解,它们就可以称为标量。相对的,如果一个数据可以继续分解,那它称为聚合量Java中最典型的聚合量是对象。如果逃逸分析证明一个对象不会被外部访问,并且这个对象是可分解的,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。拆散后的变量便可以被单独分析与优化, 可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间了。

  在Java代码运行时,通过JVM参数可指定是否开启逃逸分析, 

  -XX:+DoEscapeAnalysis : 表示开启逃逸分析
  -XX:-DoEscapeAnalysis : 表示关闭逃逸分析 从jdk 1.7开始已经默认开始逃逸分析,如需关闭,需要指定-XX:-DoEscapeAnalysis

二  同步省略(锁消除)

  在动态编译同步块的时候,即时编译器(Just-in-time Compilation,JIT)可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果同步块所使用的锁对象通过这种分析被证实只能够被一个线程访问,那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这个取消同步的过程就叫同步省略,也叫锁消除。

  如以下代码:

public void f() {
  Object hollis = new Object();
  synchronized(hollis) {
    System.out.println(hollis);
  }
}

  代码中对hollis这个对象进行加锁,但是hollis对象的生命周期只在f()方法中,每个线程进入到方法f()时,都会创建一个hollis对象,并不会被其他线程所访问到,所以在JIT编译阶段就会被优化掉。优化成:

public void f() {
  Object hollis = new Object();
  System.out.println(hollis);
}

  所以,在使用synchronized的时候,如果JIT经过逃逸分析之后发现并无线程安全问题的话,就会做锁消除。

  -XX:+EliminateLocks开启锁消除(jdk1.8默认开启,其它版本未测试)
  -XX:-EliminateLocks 关闭锁消除
  锁消除基于分析逃逸基础之上,开启锁消除必须开启逃逸分析

三  标量替换

  标量(Scalar)是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量。相对的,那些还可以分解的数据叫做聚合量(Aggregate),Java中的对象就是聚合量,因为他可以分解成其他聚合量和标量。

  在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过JIT优化,就会把这个对象拆解成若干个其中包含的若干个成员变量来代替。这个过程就是标量替换。

  public static void main(String[] args) {
    alloc();
  }  
  private static void alloc() {
    Point point = new Point(1,2);
    System.out.println("point.x="+point.x+"; point.y="+point.y);
  }
  class Point{
    private int x;
    private int y;
  }

  以上代码中,point对象并没有逃逸出alloc方法,并且point对象是可以拆解成标量的。那么,JIT就会不会直接创建Point对象,而是直接使用两个标量int x ,int y来替代Point对象。

  以上代码,经过标量替换后,就会变成:

  private static void alloc() {
    int x = 1;
    int y = 2;
    System.out.println("point.x="+x+"; point.y="+y);
  }

  可以看到,Point这个聚合量经过逃逸分析后,发现他并没有逃逸,就被替换成两个聚合量了。那么标量替换有什么好处呢?就是可以大大减少堆内存的占用。因为一旦不需要创建对象了,那么就不再需要分配堆内存了。

  标量替换为栈上分配提供了很好的基础。

四  栈上分配

  在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。

public class OnStackTest {
public static void alloc(){
byte[] b=new byte[2];
b[0]=1;
}
public static void main(String[] args) {
long b=System.currentTimeMillis();
for(int i=0;i<100000000;i++){
alloc();
}
long e=System.currentTimeMillis();
System.out.println(e-b);
}
}

开启逃逸分析,执行的时间为4毫秒。如下图:

  

关闭逃逸分析,执行的时间为618毫秒,并且伴随的大量的GC日志信息。如下图:

  

  开启逃逸分析,对象没有分配在堆上,没有进行GC,而是把对象分配在栈上。

  关闭逃逸分析,对象全部分配在堆上,当堆中对象存满后,进行多次GC,导致执行时间大大延长。堆上分配比栈上分配慢上百倍。

参考:

  1、深入分析JVM逃逸分析对性能的影响  https://blog.csdn.net/w372426096/article/details/80938788

  2、深入分析JVM逃逸分析对性能的影响  https://blog.csdn.net/jijianshuai/article/details/73740024

逃逸分析(Escape Analysis)的更多相关文章

  1. JVM笔记-逃逸分析

    参考: http://www.iteye.com/topic/473355http://blog.sina.com.cn/s/blog_4b6047bc01000avq.html 什么是逃逸分析(Es ...

  2. JVM中的逃逸分析

    逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术. 逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递 ...

  3. 基于Golang的逃逸分析(Language Mechanics On Escape Analysis)

    何为逃逸分析 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针.它涉及到指针分析和形状分析. 当一个变量(或对象)在子程序中被分配时,一个指向变量的指针 ...

  4. JVM的逃逸分析

    我们都知道Java中的对象默认都是分配到堆上,在调用栈中,只保存了对象的指针.当对象不再使用后,需要依靠GC来遍历引用树并回收内存.如果堆中对象数量太多,回收对象还有整理内存,都会会带来时间上的消耗, ...

  5. Java之JVM逃逸分析

    引言: 逃逸分析(Escape Analysis)是众多JVM技术中的一个使用不多的技术点,本文将通过一个实例来分析其使用场景. 概念 逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配 ...

  6. Golang逃逸分析

    Golang逃逸分析 介绍逃逸分析的概念,go怎么开启逃逸分析的log. 以下资料来自互联网,有错误之处,请一定告之. sheepbao 2017.06.10 什么是逃逸分析 wiki上的定义 In ...

  7. 聊聊Golang逃逸分析

    逃逸分析的概念,go怎么开启逃逸分析的log. 以下资料来自互联网,有错误之处,请一定告之. 什么是逃逸分析 wiki上的定义 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序 ...

  8. 面试问我 Java 逃逸分析,瞬间被秒杀了。。

    记得几年前有一次栈长去面试,问到了这么一个问题: Java中的对象都是在堆中分配吗?说明为什么! 当时我被问得一脸蒙逼,瞬间被秒杀得体无完肤,当时我压根就不知道他在考什么知识点,难道对象不是在堆中分配 ...

  9. Go 逃逸分析

    Go 逃逸分析 堆和栈 要理解什么是逃逸分析会涉及堆和栈的一些基本知识,如果忘记的同学我们可以简单的回顾一下: 堆(Heap):一般来讲是人为手动进行管理,手动申请.分配.释放.堆适合不可预知大小的内 ...

随机推荐

  1. response响应

    郭晨 软件151 1531610114 response1.response常用APIsetStatus:设置响应行当中的状态码setHeader:设置响应头信息getOutputStream:获得字 ...

  2. Introducing Outflux: a smart way out of InfluxDB

    转自:https://blog.timescale.com/migrate-outflux-a-smart-way-out-of-influxdb/ Migrate your workload fro ...

  3. EF提示“序列化类型为XXX的对象时检测到循环引用”

    能看到这个标题 ,我就用解释原因,网上很多,我只给大家一个解决方案会: public <#=code.Escape(entity)#> ToPOCO() { return new < ...

  4. Unity 3D入门简介

    最近在刚开始学习Unity 3D,在这里记录一下学习心得和学习笔记,边学边写,可能会比较零散.好了,废话不多说,今天从Unity 3D入门写起,主要简要介绍一下Unity 3D的和一些学习资料.以下如 ...

  5. mi家前端面经

    已经好久没想写面经了……菜鸟面到生无可恋. 1.用CSS实现下面圆形 答案: <!DOCTYPE html> <html> <head> <style typ ...

  6. 转发 Delphi中线程类TThread 实现多线程编程

    Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchr ...

  7. 一个HTTP打趴80%面试者

    面试多年,每当我问起面试者对HTTP的了解时,个个回答令我瞠目结舌,这些开发者都有3-5年的经验.请不要让我叫你野生程序员,是时候了解HTTP了,让我们当个正规军. 起因 面试官:请问你了解HTTP协 ...

  8. 64位Win7系统nbtstat 问题

    64位Win7系统nbtstat 问题 姜糖水 2015-02-02 195 阅读http://ju.outofmemory.cn/entry/114353   在32位系统写:"cmd / ...

  9. TypeScript: this bind 和 回调的正确用法

    TypeScript 中如果传递了 而且在回调函数中用了this 的话, 就要小心了, 这个this 不一定是指向当前类对象了,如果想确保指向的还是那个对象的话, 需要在传递那个方法的时候, 先调用b ...

  10. [UE4]引用Grabbable接口

    一.当前:可抓取对象的类型是GrabTargetActor 二.修改目标:可抓取对象的类型改成Grabbable. 1.Fand Grab Target的返回值改成Grabbale(变量的数据类型可以 ...