侧边栏壁纸
博主头像
再见理想博主等级

只争朝夕,不负韶华

  • 累计撰写 112 篇文章
  • 累计创建 64 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

JVM-逃逸分析

再见理想
2022-05-25 / 0 评论 / 0 点赞 / 308 阅读 / 1,359 字

问题: 对象都是分配到堆上存储吗?

一,即时编译器

我们编写的 Java 源代码通过 javac 编译成字节码文件,然后类加载器将字节码文件加载到内存中,JVM 逐行读取解释字节码翻译成对应的机器指令执行。很明显,解释器执行比那些可直接执行的二进制程序(例如 C 语言程序)慢得多。

所以为了提高效率,引入了 JIT (即时编译器)优化技术。Java 程序还是会通过解释器进行解释执行,但是如果某个方法或者代码块运行比较频繁的时候,JVM 认为这是热点代码,然后将热点代码翻译成本地机器指令,并且进行优化,缓存起来,下次再运行此段代码的时候直接运行而不用再解释。

JIT 中一个很重要的优化技术就是逃逸分析(Escape Analysis)

二,逃逸分析

逃逸分析是目前Java虚拟机中比较前沿的优化技术,这是一种可以有效减少Java程序中同步负载和堆内存分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java虚拟机能够分析出一个新对象的引用使用范围,从而决定是否一定要将此对象分配在堆内存中

逃逸分析的基本行为就是分析对象动态作用域:一个创建在方法中的对象,可能跟随着方法的返回被外部方法所引用,也可能跟随方法的参数传递至下一个方法调用,以上两种情况都称之为对象逃逸。

根据作用域可分为下面三种情况

GlobalEscape(全局逃逸):
一个对象的引用逃出了方法或者线程。例如:对象的引用赋值给类变量或者静态变量,对象跟随者方法返回至另一个方法的变量中,或者存储在一个已经逃逸的对象当中;

ArgEscape(参数级逃逸):
在方法调用过程中,对象的引用被通过方法的参数传递至下一个方法中使用。 这种状态可以通过分析被调方法的二进制代码确定;

NoEscape(没有逃逸):
一个可以进行标量替换的对象,或者对象的作用域范围就只在本方法中,随着方法栈帧的进栈而生,出栈而亡。该对象可以不被分配在传统的堆上。

开启和关闭逃逸分析

在Java代码运行时,通过JVM参数可指定是否开启逃逸分析,
-XX:+DoEscapeAnalysis : 表示开启逃逸分析
-XX:-DoEscapeAnalysis : 表示关闭逃逸分析
从jdk 1.7开始已经默认开始逃逸分析,如需关闭,需要指定 -XX:-DoEscapeAnalysis

三,为什么要做逃逸分析

为什么要进行逃逸分析,其实最终目的就是为程序做优化,提高运行性能。有如下优化技术点:锁消除、栈上分配、标量替换

锁消除

Java锁优化中的锁消除技术,其中正是通过逃逸分析技术来实现的,在动态编译同步块的时候,JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。

JIT在进行逃逸分析之后,判定同步代码块内的锁对象只能被一个线程所访问,那么JIT编译器在编译这个同步块过程中就会取消对这个代码块的同步,这个过程也就是锁的消除,也叫同步忽略。

栈上分配

在Java虚拟机中,实例化对象绝大多数情况是存储在堆内存中的,但是也有例外,一个对象在经过逃逸分析之后,发现并没有逃逸出方法时,那么这个对象可能会被优化存储在栈中,这种情况下,也并不是绝对存储在栈中,也有可能还是存储在堆中。

标量替换

标量(Scalar)是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量。相对的,那些还可以分解的数据叫做聚合量(Aggregate),Java中的对象就是聚合量,因为他可以分解成其他聚合量和标量。
JIT编译器编译阶段,如果逃逸分析发现一个对象不会被外界访问的话,那么经过JIT优化,就会把这个对象拆解成若干个其中所包含的若干个成员变量来代替,这个过程就是标量替换。

开启和关闭标量替换
-XX:+EliminateAllocations:开启标量替换(默认打开)
-XX:-EliminateAllocations: 关闭标量替换

0

评论区