我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章。

引言

我在面试别人的过程中,JVM 内存模型我几乎必问,虽然有人说问这些就是面试造航母,工作拧螺丝。如果你想当一名 CRUD 码农,你可以选择不用了解这些。

在 JVM 内存模型的问答中,有些人能说出对象是在堆上分配的。但当我问对象一定是在堆上存储的嘛时,大部分人都回答是,或者犹豫了。

其实能回答出对象是在堆上分配存储已算正确了。但随着 JIT 即时编译器的发展和逃逸分析技术的逐渐成熟,所有对象都分配到堆上也逐渐变得不那么绝对了。栈上分配标量替换锁消除等优化技术会发生一些微妙的变化。

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

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

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

逃逸分析

逃逸分析,其实就是分析一个对象是否会逃逸出方法,分析对象的动态作用域。如果一个对象在一个方法内定义,并且有可能被方法外部引用使用,那认为它逃逸了。

例如以下的 person 对象就发生了逃逸,即有可能会被方法外部引用。

public Person personEscape() {
Person person = new Person();
return person;
}

所以为什么要进行逃逸分析,其实最终目的就是为程序做优化,提高运行性能。有如下优化技术点:

  • 栈上分配
  • 标量替换
  • 锁消除

JDK1.7 开始,逃逸分析默认是开启的,可以通过以下参数进行启停。

# 开启
-XX:+DoEscapeAnalysis
# 关闭
-XX:-DoEscapeAnalysis

栈上分配

如果分析一个对象没有逃逸出方法的时候,就有可能被分配到栈上。这样就不需要在堆中进行 GC 回收,提高了性能。

package com.chenpi;

/**
* @Description
* @Author 陈皮
* @Date 2021/7/14
* @Version 1.0
*/
public class EscapeAnalysisTest { public static void main(String[] args) { long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) {
stackAlloc();
} System.out.println((System.currentTimeMillis() - startTime) + "ms");
} public static void stackAlloc() {
Person person = new Person("陈皮", 18);
} } class Person { private String name;
private long age; public Person(String name, long age) {
this.name = name;
this.age = age;
}
}

虚拟机参数设置开启逃逸分析,并且打印 GC 日志。

-Xms200m -Xmx200m -XX:+DoEscapeAnalysis -XX:+PrintGC

运行程序结果如下,消耗只需要 10 ms,并且没有 GC 。

10ms

关闭逃逸分析,并且打印 GC 日志。

-Xms200m -Xmx200m -XX:-DoEscapeAnalysis -XX:+PrintGC

运行程序结果如下,消耗时间增加了10多倍,并且伴随着多次的 GC 。

[GC (Allocation Failure)  51712K->784K(196608K), 0.0050396 secs]
[GC (Allocation Failure) 52496K->784K(196608K), 0.0030730 secs]
[GC (Allocation Failure) 52496K->752K(196608K), 0.0013993 secs]
[GC (Allocation Failure) 52464K->720K(196608K), 0.0018371 secs]
176ms

标量替换

  • 标量:不可再分解成更小数据的类型,例如基本数据类型就是标量。
  • 聚合量:可以再分解成其他聚合量或者标量的数据类型,例如对象引用类型。

如果一个对象不会发生逃逸,那么 JIT 可以优化把这个对象分解成若干个标量来代替。这就是标量替换。

public void scalarReplace() {
Coordinates coordinates = new Coordinates(105.10, 80.22);
System.out.println(coordinates.longitude);
System.out.println(coordinates.latitude);
}

以上演示程序,coordinates 对象不会发生逃逸,所以 JIT 编译器可以使用标量替换进行优化。最终被优化成如下程序。

public void scalarReplace() {
System.out.println(105.10);
System.out.println(80.22);
}

其实在现有的虚拟机中,并没有真正的实现栈上分配,其实是通过标量替换来实现的。

锁消除

为什么要消除锁呢?因为加锁会降低性能,那如何不用加锁是最好的。如果分析出加锁的对象不会发生逃逸,即只能被一个线程访问,JIT 是可以优化消除这个锁的。也称为同步省略。

public void lockRemove() {
synchronized (new Object()) {
System.out.println("我是陈皮!");
}
}

以上演示程序,Object 对象不会发生逃逸,所以也只能当前线程访问到,所以 JIT 编译器可以进行优化锁消除。最终被优化成如下程序。

public void lockRemove() {
System.out.println("我是陈皮!");
}

总结

但随着 JIT 即时编译器的发展和逃逸分析技术的逐渐成熟,所有对象都分配到堆上也逐渐变得不那么绝对了。通过逃逸分析技术,对象可能被分配到栈上,能减少 GC,提高程序性能。

但是开启逃逸分析的程序的性能一定高于没有开启逃逸分析的性能吗?其实不一定。逃逸分析技术其实也是很复杂的,所以也是一个会耗时的过程,如果经过逃逸分析之后,发现所有对象都逃逸了,就不能做优化处理,那这个逃逸分析的过程就消耗了时间,还不起优化作用,得不偿失。

如果面试官问你 JVM,额外回答逃逸分析技术会让你加分!的更多相关文章

  1. 面试官问我JVM内存结构,我真的是

    面试官:今天来聊聊JVM的内存结构吧? 候选者:嗯,好的 候选者:前几次面试的时候也提到了:class文件会被类加载器装载至JVM中,并且JVM会负责程序「运行时」的「内存管理」 候选者:而JVM的内 ...

  2. 面试官问我JVM调优,我忍不住了!

    面试官:今天要不来聊聊JVM调优相关的吧? 面试官:你曾经在生产环境下有过调优JVM的经历吗? 候选者:没有 面试官:... 候选者:嗯...是这样的,我们一般优化系统的思路是这样的 候选者:1. 一 ...

  3. 每日一问:面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

    面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

  4. 【MySQL】面试官问我:MySQL如何实现无数据插入,有数据更新?我是这样回答的!

    写在前面 马上就是金九银十的跳槽黄金期了,很多读者都开始出去面试了.这不,又一名读者出去面试被面试官问了一个MySQL的问题:向MySQL中插入数据,如何实现MySQL中没有当前id标识的数据时插入数 ...

  5. 「每日一题」有人上次在dy面试,面试官问我:vue数据绑定的实现原理。你说我该如何回答?

    关注「松宝写代码」,精选好文,每日一题 ​时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 来源:原创 一.前言 文章首发在「松宝写代码」 2020. ...

  6. 美女面试官问我Python如何优雅的创建临时文件,我的回答....

    [摘要] 本故事纯属虚构,如有巧合,他们故事里的美女面试官也肯定没有我的美,请自行脑补... 小P像多数Python自学者一样,苦心钻研小半年,一朝出师投简历. 这不,一家招聘初级Python开发工程 ...

  7. [每日一题]面试官问:谈谈你对ES6的proxy的理解?

    [每日一题]面试官问:谈谈你对ES6的proxy的理解? 关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...

  8. 面试官问我,Redis分布式锁如何续期?懵了。

    前言 上一篇[面试官问我,使用Dubbo有没有遇到一些坑?我笑了.]之后,又有一位粉丝和我说在面试过程中被虐了.鉴于这位粉丝是之前肥朝的粉丝,而且周一又要开启新一轮的面试,为了回馈他长期以来的支持,所 ...

  9. 面试官问你JS基本类型时他想知道什么?

    面试的时候我们经常会被问答js的数据类型.大部分情况我们会这样回答包括:1.基本类型(值类型或者原始类型): Number.Boolean.String.NULL.Undefined以及ES6的Sym ...

随机推荐

  1. 血缘关系分析工具SQLFLOW--实践指南

    SQLFlow 是用于追溯数据血缘关系的工具,它自诞生以来以帮助成千上万的工程师即用户解决了困扰许久的数据血缘梳理工作. 数据库中视图(View)的数据来自表(Table)或其他视图,视图中字段(Co ...

  2. [Django高级之Auth模块]

    [Django高级之Auth模块] auth模块 ←详情点击查看 1.Auth模块是什么 Auth模块是Django自带的用户认证模块: 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统 ...

  3. 2. Servlet3.0注解方式 @WebServlet

    web.xml配置还是比较麻烦,这次使用注解方式 编写servlet import javax.servlet.ServletException; import javax.servlet.annot ...

  4. NVIDIA DGX SUPERPOD 企业解决方案

    NVIDIA DGX SUPERPOD 企业解决方案 实现大规模 AI 创新的捷径 NVIDIA DGX SuperPOD 企业解决方案是业界首个支持任何组织大规模实施 AI 的基础架构解决方案.这一 ...

  5. 如何在小型pcb的移动设备上获得更好的无线性能

    如何在小型pcb的移动设备上获得更好的无线性能 How to get better wireless performance for mobile devices with small PCBs 小型 ...

  6. SpringCloud Alibaba实战(5:子模块基本业务开发)

    源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 在上一节里,我们搭建了一个微服务项目的整体架构,并进行了版本控制. 接下来我们进一步 ...

  7. 阿里云视频云 Retina 多媒体 AI 体验馆开张啦!

    带你体验视频更多可能 海量视频管理难度大?翻库检索特定人物费时费力?视频内容剪辑效率低?您的得力助手"Retina多媒体AI"体验馆已上线.带你感受视频AI黑科技,开启极致智能体验 ...

  8. 一张图理清计算机常见编码的关系。ASCII、Unicode都不是事儿

    编码按适用范围可以简单分为:(本人自定义) 美国编码(ASCII)ASCII为基础编码,来源于美国:其它编码都兼容ASCII编码: 欧盟编码(ISO8859-1.WINDOWS-1252)先是ISO- ...

  9. 04:Django生命周期流程图

  10. klayout安装及使用教程

    klayout 版本:klayout-0.26.9 我的系统环境:Deepin20(可以视为Debian) 修改过的代码地址:https://github.com/stuartofmine/klayo ...