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

逃逸分析

内存逃逸主要是对象的动态作用域的改变而引起的,故而内存逃逸的分析就是分析对象的动态作用域。

发生逃逸行为的情况分为两种:方法逃逸和线程逃逸

方法逃逸

当方法创建了一个对象之后,这个对象被外部方法所调用,这个时候方法运行结束要进行GC时,本该方法的对象被回收,却发现该对象还存活着,没法回收,则称为 "方法逃逸"

简单来说:就是当前方法创建的对象,本该是当前方法的栈帧所管理,却被调用方所使用,可以称之为内存逃逸

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

a9761afb77b0c7533533ff353d970ed6.png

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

85264a3ac50cf0549088287b253112ce.png

线程逃逸

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

栈上分配

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

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

简单来说:比如说,我上一篇文章有写到,一个方法对应一个栈帧,而我的对象是在当前的栈帧中所管理的,并非逃逸到方法外,所以创建的对象是在栈中,而非堆中,所以称为 "栈上分配"

同步消除

线程同步本身比较耗时,若确定了一个变量不会逃逸出线程,无法被其他线程访问到,那这个变量的读写就不会存在竞争,则可以消除对该对象的同步锁

标量替换

1、标量是指不可分割的量,如java中基本数据类型和reference类型,都不能再进一步分解,他们就可以称为标量。

2、若一个数据可以继续分解,那就称之为聚合量,而对象就是典型的聚合量。

3、若逃逸分析证明一个对象不会逃逸出方法,不会被外部访问,并且这个对象是可以被分解的,那程序在真正执行的时候可能不创建这个对象,而是直接创建这个对象分解后的标量来代替。这样就无需在对对象分配空间了,只在栈上为分解出的变量分配内存即可。

注意:

逃逸分析是比较耗时的,所以性能未必提升很多,因为其耗时性,采用的算法都是不那么准确但是时间压力相对较小的算法来完成的,这就可能导致效果不稳定,要慎用。

由于HotSpot虚拟机目前的实现方法导致栈上分配实现起来比较复杂,所以HotSpot虚拟机中暂时还没有这项优化。

相关JVM参数

-XX:+DoEscapeAnalysis 开启逃逸分析、

-XX:+PrintEscapeAnalysis 开启逃逸分析后,可通过此参数查看分析结果。

-XX:+EliminateAllocations 开启标量替换

-XX:+EliminateLocks 开启同步消除

-XX:+PrintEliminateAllocations 开启标量替换后,查看标量替换情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值