0
点赞
收藏
分享

微信扫一扫

JVM系列之逃逸分析

Sophia的玲珑阁 2021-09-18 阅读 123

一、前言

答案是否定的,Java对象的创建一般情况是在堆内存中,当堆内存空间不足时,就会触发GC回收,GC次数多了就会影响到程序的性能;
因此逃逸分析就是为了缓解这一问题而诞生的。

二、什么是逃逸分析

因为堆内存空间总是有限的,随着对象的积累,gc的次数会越来越频繁,这显然会影响到程序的整体性能;

逃逸分析的目的是分析新创建对象的使用范围,并决定对象是否可以分配在Java堆上;

三、逃逸状态

  1. 全局逃逸(GlobalEscape):一个对象的作用范围逃出了当前方法或者当前线程
  1. 参数逃逸(ArgEscape):一个对象被作为方法参数传递或者被参数引用,但在调用过程中不会发生全局逃逸,这种状态可以通过分析被调方法的二进制代码确定

  2. 没有逃逸(NoEscape):顾名思义,就是没有逃逸

四、逃逸分析的作用

示例代码:

/**
 * 测试逃逸分析
 *
 * @author 小白
 * @date 2021/4/3 14:31
 */
public class Test {
  public static void main(String[] args) throws InterruptedException {
    long t1 = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
      getObject();
    }
    long t2 = System.currentTimeMillis();
    System.out.println(t2 - t1);
    TimeUnit.MINUTES.sleep(10);
  }

  private static void getObject() {
    Object obj = new Object();
  }
}

开启逃逸分析:


关闭逃逸分析:


分析:

开启逃逸分析时,jvm中大概产生11万左右的对象,代码耗时3ms,关闭逃逸分析时,大概产生60w左右的对象,代码耗时428ms,比较来看,看起逃逸分析是有助于虚拟机性能提升的。

1.标量替换

基础类型和对象的引用可以理解为标量(不能被进一步分解);
对象就是聚合量,能被进一步分解。

用其成员变量(分散的标量)代替该对象整体,从而不需要连续的存储空间,就可以将对象的部分甚至全部都保存在CPU寄存器内,这就叫做标量替换。

如果一个对象没有发生逃逸,那就不用创建这个对象,虚拟机只会在栈或者寄存器上创建它会使用到的成员标量,节省了内存空间,从而提升了应用程序性能。

java -XX:+DoEscapeAnalysis -XX:+EliminateAllocations Test
代码执行时间:3 ms

java -XX:+DoEscapeAnalysis -XX:-EliminateAllocations Test
代码执行时间:415 ms

java -XX:-DoEscapeAnalysis -XX:+EliminateAllocations Test
代码执行时间:392 ms

java -XX:-DoEscapeAnalysis -XX:-EliminateAllocations Test
代码执行时间:387 ms

分析

开启和关闭标量替换对于程序性能的影响很大,同时也验证了标量替换功能生效的前提是逃逸分析已经开启,否则没有意义。

2. 锁消除

示例代码:

  private static void getObject() {
    Object obj = new Object();
    synchronized (obj) {
      obj.toString();
    }
  }

java -XX:+DoEscapeAnalysis -XX:+EliminateLocks Test
代码执行时间:4 ms

java -XX:+DoEscapeAnalysis -XX:-EliminateLocks Test
代码执行时间:388 ms

分析

开启和关闭锁消除对于程序性能的影响也是很大。

3.栈内存分配

将原本分配在堆内存上的对象转而分配在栈内存上,减少堆内存的占用,从而减少 GC 的次数

五、非逃逸分析优化

  1. 锁粗化
    当连续获取同一个对象的锁时,HotSpot虚拟机会去检查多个锁区域是否能合并成一个更大的锁区域。这种聚合被称作锁粗化,它能够减少加锁和解锁的消耗。

  2. 嵌套锁
    同步块可能会一个嵌套一个,进而两个块使用同一个对象的监视器锁来进行同步也是很有可能的。这种情况我们称之为嵌套锁,HotSpot虚拟机是可以识别出来并删除掉内部块中的锁的。当一个线程进入外部块时就已经获取到锁了,因此当它尝试进入内部块时,肯定也仍持有这个锁,所以这个时候删除锁是可行的。

总结

开发中尽量使用局部变量的,就不要使用在方法外定义。

举报

相关推荐

0 条评论