JVM-彻底搞懂 逃逸分析&标量替换

简介: JVM-彻底搞懂 逃逸分析&标量替换

20200627112124586.png

Pre

JVM-剖析对象内存分配流程


对象分配流程总览


20200627001636884.png


逃逸分析所处的阶段


通过上图的对象分配流程,我们可以知道逃逸分析是发生在第一步判断对象是否可以在栈上分配的时候, 在栈上分配的目的是为了减少将对象分配到堆上的概率,节约堆内存,减少GC压力。

逃逸分析是JVM为了优化对象分配而做的一种优化措施。


示例说明逃逸分析的含义


那逃逸分析的标准是什么呢? 经过逃逸分析以后什么样的对象可以在栈上分配,什么样的对象不可以在栈上分配呢?


JVM通过逃逸分析确定该对象不会被外部访问。如果不会逃逸可以将该对象在栈上分配内存,这样该对象所占用的内存空间就可以随栈帧出栈而销毁,减轻GC的压力。

对象逃逸分析就是分析对象动态作用域 。 当一个对象在方法中被定义后,它可能被外部方法所引用。

举个例子

public User doSomething1() {
   Artisan artisan1= new Artisan();
   artisan1.setId(1);
   artisan1.setDesc("artisan1");
   // ......
   return user;
}
public void doSomething2() {
   Artisan artisan2= new Artisan();
   artisan2.setId(2);
   artisan2.setDesc("artisan2");
   // ...... 
}


doSomething1返回对象,在方法结束之后被返回了,那这个对象的作用域范围是不确定的。


doSomething2方法可以非常确定的是当方法结束以后,artisan2这个对象就失效了,因为它的作用域范围是当前方法。 那对于这样的对象,JVM会把这样的对象分配到线程栈中,让它随着方法结束时跟随线程栈内存一起被回收掉。


逃逸分析的对象分配的方式【标量替换】

标量替换的含义


通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。


开启标量替换参数(-XX:+EliminateAllocations),JDK7之后默认开启


标量 VS 聚合量


标量替换 ? 那什么是标量 ?

  • 标量: 不可被进一步分解的量,而JAVA的基本数据类型就是标量(比如int,long等基本数据类型以及reference类型等) 。聚合量: 标量的对立就是可以被进一步分解的量,称之为聚合量。 在JAVA中对象就是可以被进一步分解的聚合量。

JVM 参数 -XX:+DoEscapeAnalysis & -XX:+EliminateAllocations


JDK7之后默认开启逃逸分析 .

如果需要关闭逃逸分析 -XX:-DoEscapeAnalysis 即可,不推荐修改该参数。

-XX:+EliminateAllocations 开启标量替换参数 . 该参数的前提是开启了逃逸分析,如果没有开启逃逸分析,仅开启该参数无效。


栈上分配Demo

Code

public class Artisan {
    private int id ;
    private String desc ;
    // set get
}
/**
 * 栈上分配,标量替换
 *
 * 示例代码调用了1亿次alloc(),如果是分配到堆上,大概需要1GB以上堆空间,如果堆空间小于该值,必然会触发GC。
 *
 * 使用如下参数不会发生GC
 * -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
 *
 * 使用如下参数都会发生大量GC
 * -Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
 * -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations
 */
public class AllocationOnThreadStackTest {
    public static void main(String[] args) throws InterruptedException {
        long begin = System.currentTimeMillis();
        allocate();
        System.out.println("cost:" + (System.currentTimeMillis() - begin));
    }
    private static void allocate() throws InterruptedException {
        for (int i = 0; i < 100000000; i++) {
            Artisan artisan = new Artisan();
            artisan.setId(1);
            artisan.setDesc("artisan");
        }
    }
}

【默认开启逃逸分析】

-Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations


【默认开启逃逸分析】

-Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations


20200627120402185.png


只有在程序开始之前GC了一次 (这是JVM内部复杂的机制决定的), 运行过程中,并没有发生Minor GC .


【关闭逃逸分析】

看下关闭逃逸分析的效果

  • -DoEscapeAnalysis +EliminateAllocations
-Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations

20200627120643783.png20200627182430407.png

发生了578次 Minor GC , 耗时 1369毫秒。 可见GC过多,对性能的影响是非常大的。


+DoEscapeAnalysis -EliminateAllocations

再看看 -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations

20200627182737165.png


综上所述,开启了逃逸分析,对堆内存的节省,减少GC压力,提高程序性能大有裨益。

最后说一句, 栈上空间不足,那一定会往堆上分配。

相关文章
|
7天前
|
缓存 Java
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
11 0
|
8天前
|
Java UED 开发者
JVM逃逸分析原理解析:优化Java程序性能和内存利用效率
JVM逃逸分析原理解析:优化Java程序性能和内存利用效率
|
2月前
|
算法 安全 Java
【JVM】并发的可达性分析详细解释
【JVM】并发的可达性分析详细解释
|
2月前
|
算法 Java
深入浅出JVM(十六)之三色标记法与并发可达性分析
深入浅出JVM(十六)之三色标记法与并发可达性分析
|
2月前
|
存储 Arthas 监控
JVM工作原理与实战(三十):堆内存状况的对比分析
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了堆内存状况的对比分析、产生内存溢出的原因等内容。
28 0
|
2月前
|
监控 算法 安全
JVM工作原理与实战(二十三):堆的垃圾回收-引用计数法和可达性分析法
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了判断堆上的对象是否可以回收的方法(引用计数法、可达性分析法)、查看垃圾回收日志等内容。
25 0
|
2月前
|
存储 算法 Java
精华推荐 | 【JVM深层系列】「GC底层调优专题」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)
精华推荐 | 【JVM深层系列】「GC底层调优专题」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)
70 0
|
3天前
|
缓存 算法 Java
JVM内存溢出(OutOfMemory)异常排查与解决方法
JVM内存溢出(OutOfMemory)异常排查与解决方法
|
11天前
|
存储 Java C++
Java虚拟机(JVM)在执行Java程序时,会将其管理的内存划分为几个不同的区域
【6月更文挑战第24天】Java JVM管理内存分7区:程序计数器记录线程执行位置;虚拟机栈处理方法调用,每个线程有独立栈;本地方法栈服务native方法;Java堆存储所有对象实例,垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息;运行时常量池存储常量;直接内存不属于JVM规范,通过`java.nio`手动管理,不受GC直接影响。
20 5
|
10天前
|
存储 Java 对象存储
jvm内存模型剖析
当线程cpu时间片执行完后,线程进入休眠状态,当再次唤醒时,通过程序计数器确定指令执行到哪一行,然后继续往下执行。
19 1