JVM底层原理之什么是逃逸分析
介绍:
逃逸分析(Escape Analysis)根据运行状态可以判断方法中的变量 , 是否会被外部方法引用或外部线程访问 , 如果会 , 那么这个行为就是逃逸 , 根据情况可以分为两种逃逸:
方法逃逸
当一个方法中的变量可能被外部方法所引用 , 比方说作为调用参数 , 将其传递到其他的子方法中 , 这种可以称为方法逃逸 。
【jvm|JVM底层原理之什么是逃逸分析】线程逃逸
如果是多线程环境 , 有的变量没有作线程保护 , 可能会被外部线程访问到 , 这种行为称为线程逃逸 。
也就是这项技术并不是什么优化技术 , 而是进行某些专项优化前的必要分析 , 如果不会发生逃逸现象 , 才进行一些专项的优化 。 另外这项技术是从JDK1.6开始的 , 所以目前而言不算太成熟 , 因为逃逸分析需要一些复杂的运算 , 又要兼顾性能的损耗 , 所以会有一些误差 。
参数设置:
可以通过JVM参数进行设置开启或关闭逃逸分析:
-XX:+DoEscapeAnalysis 开启逃逸分析(jdk1.8 默认开启)-XX:-DoEscapeAnalysis 关闭逃逸分析
它手下的优化方法主要有三种:栈上分配
、锁消除
、标量替换
。 也就是如果你没开启逃逸分析 , 那么就相当于关闭了这三种优化手段了 。
a.栈上分配(Stack Allocations)如果确定一个变量不会逃逸出方法外 , 那么C2会让这个对象直接在栈上分配内存创建实例 , 而不是在堆上 , 因为这样一来可以加快速度 , 二来是该变量占用的内存空间可以随着栈帧出栈而销毁 。
一般应用中 , 大多局部对象都可以使用栈上分配 , 这样垃圾收集器的压力就会小很多 。
b.同步消除(Synchronization Elimination)又名锁消除 , 多线程的情况下 , 有些变量会上同步锁 , 避免发生线程冲突问题 。 但是经过了逃逸分析后 , 该变量分析确定了它不会逃逸出线程 , 不会被其他线程访问到 , 那这个变量就没有必要上什么同步锁了 , 就可以清除掉这个变量的同步锁以节约资源 。
比较常用的例子是StringBuffer的append()方法 , 由于加了修饰符synchronized , 所以是线程安全的 , 但是如果你是单线程环境 , 就完全没有必要上这个锁了 。 所以开启了逃逸分析和锁消除的情况下(还要是C2才行) , 优化时就会将同步锁清除 , 大大的提升效率 。
参数:
-XX:+EliminateLocks 开启锁消除(jdk1.8 默认开启)-XX:-EliminateLocks 关闭锁消除
c.标量替换(Scalar Replacement)标量(Scalar)是指一个数据已经无法再分解成更小的数据来表示了 , Java中的原始数据类型(int、long等数值类型以及reference类型等)不能在进一步分解 , 这些就可以称为标量 。
如果把一个Java对象拆散 , 根据程序访问的情况 , 将其使用到的成员变量恢复原始类型来访问就叫做标量替换 。
聚合量:
相对的 , 如果一个数据可以继续分解 , 那它就称作聚合量(Aggregate) , Java中的Object对象就是最典型的聚合量 。
如果逃逸分析证明一个对象不会被外部访问的话 , 在优化时就可能进行标量替换 。 也就是在程序执行时 , 将不会创建整个对象 , 而是拆分为只创建它使用到的几个成员变量的标量 。 将对象拆分后 , 还可能结合栈上分配 , 将这些基本标量直接在栈上创建实例 , 可以为后续进一步的优化手段创建条件 。
这里用java语言举个例子 , 当然实际情况jvm是直接读取class文件的:
- 荣耀|于无声处惊雷,荣耀笔记本从底层再突破
- 荣耀|解读荣耀PC新战略:PC行业底层创新的时代来了
- MySQL|逆向思维:反方向销售,所有营销高手都懂的底层逻辑
- 算法|JVM有几种垃圾回收(GC)算法,你知道吗?
- 电池|这台iPhone仅换过电池?网友:屏幕一看就有问题,还改了底层数据!
- jvm|Java:了解JVM加载、JVM链接和JVM初始化
- 安卓|安卓13开发者版本曝光!OPPO Find N已适配,系统底层迎来大升级
- 详解裂变的底层逻辑,裂变模型与驱动力
- vivo|Java:了解JVM加载、JVM链接和JVM初始化
- jvm|JVM底层原理之JIT编译器如何通过方法内联优化代码