Java逃逸分析详解及代码示例

 更新时间:2017年11月10日 10:33:19   作者:StormMa  
这篇文章主要介绍了Java逃逸分析详解及代码示例,具有一定参考价值,需要的朋友可以了解下。

概念引入

我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会导致Java中创建出来的对象并一定分别在所认为的堆上。这两个点分别是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer)线程私有的缓存区。

基本概念介绍

逃逸分析,是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。

在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他过程或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。通俗点讲,如果一个对象的指针被多个方法或者线程引用时,那么我们就称这个对象的指针发生了逃逸。

Java在Java SE 6u23以及以后的版本中支持并默认开启了逃逸分析的选项。Java的 HotSpot JIT编译器,能够在方法重载或者动态加载代码的时候对代码进行逃逸分析,同时Java对象在堆上分配和内置线程的特点使得逃逸分析成Java的重要功能。

代码示例

package me.stormma.gc;
/**
 * <p>Created on 2017/4/21.</p>
 *
 * @author stormma
 *
 * @title <p>逃逸分析</p>
 */
public class EscapeAnalysis {
  public static B b;
  /**
   * <p>全局变量赋值发生指针逃逸</p>
   */
  public void globalVariablePointerEscape() {
    b = new B();
  }
  /**
   * <p>方法返回引用,发生指针逃逸</p>
   * @return
   */
  public B methodPointerEscape() {
    return new B();
  }
  /**
   * <p>实例引用发生指针逃逸</p>
   */
  public void instancePassPointerEscape() {
    methodPointerEscape().printClassName(this);
  }
  class B {
    public void printClassName(EscapeAnalysis clazz) {
      System.out.println(clazz.getClass().getName());
    }
  }
}

逃逸分析研究对于 java 编译器有什么好处呢?我们知道 java 对象总是在堆中被分配的,因此 java 对象的创建和回收对系统的开销是很大的。java 语言被批评的一个地方,也是认为 java 性能慢的一个原因就是 java不支持栈上分配对象。JDK6里的 Swing内存和性能消耗的瓶颈就是由于 GC 来遍历引用树并回收内存的,如果对象的数目比较多,将给 GC 带来较大的压力,也间接得影响了性能。减少临时对象在堆内分配的数量,无疑是最有效的优化方法。java 中应用里普遍存在一种场景,一般是在方法体内,声明了一个局部变量,并且该变量在方法执行生命周期内未发生逃逸,按照 JVM内存分配机制,首先会在堆内存上创建类的实例(对象),然后将此对象的引用压入调用栈,继续执行,这是 JVM优化前的方式。当然,我们可以采用逃逸分析对 JVM 进行优化。即针对栈的重新分配方式,首先我们需要分析并且找到未逃逸的变量,将该变量类的实例化内存直接在栈里分配,无需进入堆,分配完成之后,继续调用栈内执行,最后线程执行结束,栈空间被回收,局部变量对象也被回收,通过这种方式的优化,与优化前的方案主要区别在于对象的存储介质,优化前是在堆中,而优化后的是在栈中,从而减少了堆中临时对象的分配(较耗时),从而优化性能。

使用逃逸分析进行性能优化(-XX:+DoEscapeAnalysis开启逃逸分析)

public void method() {
  Test test = new Test();
  //处理逻辑
  ......
  test = null;
}

这段代码,之所以可以在栈上进行内存分配,是因为没有发生指针逃逸,即是引用没有暴露出这个方法体。

栈和堆内存分配比较

package me.stormma.gc;
/**
 * <p>Created on 2017/4/21.</p>
 *
 * @author stormma
 * @description: <p>内存分配比较</p>
 */
public class EscapeAnalysisTest {
  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);
  }
}

JVM 参数为-server -Xmx10m -Xms10m -XX:-DoEscapeAnalysis -XX:+PrintGC, 运行结果

JVM 参数为-server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC, 运行结果

性能测试

package me.stormma.gc;
/**
 * <p>Created on 2017/4/21.</p>
 *
 * @author stormma
 *
 * @description: <p>利用逃逸分析进行性能优化</p>
 */
public class EscapeAnalysisTest {
  private static class Foo {
    private int x;
    private static int counter;
    public Foo() {
      x = (++counter);
    }
  }
  public static void main(String[] args) {
    long start = System.nanoTime();
    for (int i = 0; i < 1000 * 1000 * 10; ++i) {
      Foo foo = new Foo();
    }
    long end = System.nanoTime();
    System.out.println("Time cost is " + (end - start));
  }
}

使用逃逸分析优化 JVM输出结果( -server -XX:+DoEscapeAnalysis -XX:+PrintGC)

Time cost is 11012345

未使用逃逸分析优化 JVM 输出结果( -server -Xmx10m -Xms10m -XX:-DoEscapeAnalysis -XX:+PrintGC)

[GC (Allocation Failure) 33280K->408K(125952K), 0.0010344 secs]
[GC (Allocation Failure) 33688K->424K(125952K), 0.0009799 secs]
[GC (Allocation Failure) 33704K->376K(125952K), 0.0007297 secs]
[GC (Allocation Failure) 33656K->456K(159232K), 0.0014817 secs]
Time cost is 68562263

分析结果,性能优化1/6。

总结

以上就是本文关于Java逃逸分析详解及代码示例的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:Java实现生产者消费者问题与读者写者问题详解java算法实现红黑树完整代码示例Java编程接口调用的作用及代码分享等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持。

相关文章

  • 在JPA中criteriabuilder使用or拼接多个like语句

    在JPA中criteriabuilder使用or拼接多个like语句

    这篇文章主要介绍了在JPA中criteriabuilder使用or拼接多个like语句,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • MySqlConnector的使用教程

    MySqlConnector的使用教程

    本文详细介绍了MySqlConnector的核心功能,包括数据变更捕获、KafkaConnect兼容性、配置管理、版本信息、连接器任务创建、配置验证、数据库连接建立和连接器配置创建等,感兴趣的可以了解一下
    2024-10-10
  • SpringBoot后端解决跨域问题的3种方案分享

    SpringBoot后端解决跨域问题的3种方案分享

    这篇文章主要给大家分享介绍了关于SpringBoot后端解决跨域问题的3种方案,跨域指的是浏览器不能执行其他网站的脚本,它是由浏览器的同源策略造成的,是浏览器施加的安全限制,需要的朋友可以参考下
    2023-07-07
  • Spring boot2.x中集成H2数据库代码实例

    Spring boot2.x中集成H2数据库代码实例

    这篇文章主要介绍了Spring boot2.x中集成H2数据库代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • java遍历http请求request的所有参数实现方法

    java遍历http请求request的所有参数实现方法

    下面小编就为大家带来一篇java遍历http请求request的所有参数实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • 浅谈Java 继承接口同名函数问题

    浅谈Java 继承接口同名函数问题

    这篇文章主要介绍了浅谈Java 继承接口同名函数问题。具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • springSecurity之如何添加自定义过滤器

    springSecurity之如何添加自定义过滤器

    这篇文章主要介绍了springSecurity之如何添加自定义过滤器的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • springboot如何使用sm2加密传输

    springboot如何使用sm2加密传输

    这篇文章主要介绍了springboot如何使用sm2加密传输问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • Java 8 对 HashSet 元素进行排序的操作方法

    Java 8 对 HashSet 元素进行排序的操作方法

    Java 中HashSet是一个不保证元素顺序的集合类,其内部是基于 HashMap 实现的,HashSet不支持排序,我们在需要对HashSet 排序时,必须将其转换为支持排序的集合或数据结构,如 List,本文将详细介绍在 Java 8 中如何对 HashSet 中的元素进行排序,感兴趣的朋友一起看看吧
    2024-11-11
  • mybatis自动生成@Table、@Column、@Id注解的方法

    mybatis自动生成@Table、@Column、@Id注解的方法

    这篇文章主要介绍了mybatis自动生成@Table、@Column、@Id注解的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12

最新评论