美文网首页
JVM内存逃逸与栈上分配,程序员必须掌握的知识

JVM内存逃逸与栈上分配,程序员必须掌握的知识

作者: Sunny捏 | 来源:发表于2020-06-18 15:19 被阅读0次
    逃逸分析
    内存逃逸主要是对象的动态作用域的改变而引起的,故而内存逃逸的分析就是分析对象的动态作用域。
    发生逃逸行为的情况分为两种:方法逃逸和线程逃逸
    
    方法逃逸
    当方法创建了一个对象之后,这个对象被外部方法所调用,这个时候方法运行结束要进行GC时,本该方法的对象被回收,却发现该对象还存活着,没法回收,则称为 "方法逃逸"
    简单来说:就是当前方法创建的对象,本该是当前方法的栈帧所管理,却被调用方所使用,可以称之为内存逃逸
    

    下图中,可以看到直接将User对象返回出去,这样这个User对象有可能会被其他地方所改变,这样他的作用域就不只是在方法内部了,这样就是逃逸到方法外部了

    那怎么样才能够不让方法逃逸呢?很简单,就不返回User对象即可,看下图

    线程逃逸
    上面的例子,直接将对象进行返回出去,该对象很有可能被外部线程所访问,如:赋值给变量等等 则称为 "线程逃逸
    
    栈上分配

    如果能够证明一个对象,不会进行逃逸到方法或线程外的话,则可以对该变量进行优化

    当我们创建一个对象的时候,会立马想到该对象是会存储到堆空间中的,而垃圾回收机制会在堆空间中回收不再使用的对象,但是筛选可回收对象,还有整理对象都需要消耗时间,如果能够通过逃逸分析确定某些对象不会逃出到方法外的话,那么就可以直接让这个对象在栈空间分配内存,这样该对象会随着方法的执行完毕自动进行销毁

    简单来说:比如说,我上一篇文章有写到,一个方法对应一个栈帧,而我的对象是在当前的栈帧中所管理的,并非逃逸到方法外,所以创建的对象是在栈中,而非堆中,所以称为 "栈上分配"
    
    同步消除
    线程同步本身比较耗时,若确定了一个变量不会逃逸出线程,无法被其他线程访问到,那这个变量的读写就不会存在竞争,则可以消除对该对象的同步锁
    
    标量替换
    1、标量是指不可分割的量,如java中基本数据类型和reference类型,都不能再进一步分解,他们就可以称为标量。
    2、若一个数据可以继续分解,那就称之为聚合量,而对象就是典型的聚合量。
    3、若逃逸分析证明一个对象不会逃逸出方法,不会被外部访问,并且这个对象是可以被分解的,那程序在真正执行的时候可能不创建这个对象,而是直接创建这个对象分解后的标量来代替。这样就无需在对对象分配空间了,只在栈上为分解出的变量分配内存即可。
    

    注意:

    逃逸分析是比较耗时的,所以性能未必提升很多,因为其耗时性,采用的算法都是不那么准确但是时间压力相对较小的算法来完成的,这就可能导致效果不稳定,要慎用。
    由于HotSpot虚拟机目前的实现方法导致栈上分配实现起来比较复杂,所以HotSpot虚拟机中暂时还没有这项优化。

    相关JVM参数

    -XX:+DoEscapeAnalysis 开启逃逸分析、
    -XX:+PrintEscapeAnalysis 开启逃逸分析后,可通过此参数查看分析结果。
    -XX:+EliminateAllocations 开启标量替换
    -XX:+EliminateLocks 开启同步消除
    -XX:+PrintEliminateAllocations 开启标量替换后,查看标量替换情况。

    相关文章

      网友评论

          本文标题:JVM内存逃逸与栈上分配,程序员必须掌握的知识

          本文链接:https://www.haomeiwen.com/subject/wxyuxktx.html