目录

编译器优化技术之语言相关的优化技术

简介

前置问题

对象是否都被分配到了堆内存中?

为什么会发生逃逸?

什么是逃逸分析?

逃逸分析原理

什么是方法逃逸?

举例说明:

什么是线程逃逸?

举例说明:

对象的逃逸程度

逃逸分析的优化方法

1.栈上分配(Stack Allocations)

2.标量替换(Scalar Replacement)

举例说明:

3.同步消除(Synchronization Elimination)[锁消除]

举例说明:

总结

逃逸分析设置JVM参数配置


编译器优化技术之语言相关的优化技术

简介

本文主要说明JVM虚拟机中编译器优化技术之一,逃逸分析。 也是最前言的优化技术之一。

前置问题

对象是否都被分配到了堆内存中?

众所周知,Java对象都默认被分配到堆中,栈中只保存了对象的指引,当对象不再使用后,需要依赖GC来遍历引用树并回收内存。但并不是所有对象都被分配到了堆中,也会被分配到栈中,看完本文就明白为什么会分配到栈中了。

为什么会发生逃逸?

在计算及语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。

当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸。

 通俗来讲,如果一个对象的指针被多个方法或者线程所引用时,就称这个变量的指针(或者对象)的逃逸。

举例说明一下:

public StringBuilder str(String a, String b){StringBuilder s = new StringBuilder();s.append(a);s.append(b);return s;
}

变量s是在方法的内部变量,而此时它被直接返回,这样s就有可能被其他地方的方法或参数所该改变,这样它的作用域就不仅仅是str方法了,虽然它是一个局部变量,但其实是发生了“逃逸”。

改造一下代码;(避免逃逸)

public StringBuilder str(String a, String b){StringBuilder s = new StringBuilder();s.append(a);s.append(b);return s.toString();
}

将返回改为s.toString(),此时就没有直接返回s,而是toString(),那么s就没有从方法中直接脱离,就没有发生逃逸。

什么是逃逸分析?

逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术,它与类型继承关系分析一样,并不是直接优化代码的手段,而是为其他优化措施提供依据的分析技术。

是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。

逃逸分析原理

分析对象动态作用域,当一个对象在方法里面被定义后,它可能被外部方法所引用.

Java本身的限制(对象只能分配到堆中),为了减少临时对象在堆内分配的数量,在一个方法体内定义一个局部变量,并且该变量在方法执行过程中未发生逃逸,

--> 按照JVM调优机制,首先会在堆内存创建类的实例,

--> 然后将此对象的引用压入调用栈,继续执行,

--> 首先找出未逃逸的变量,将该变量直接存到栈里,无需进入堆,

--> 分配完成后,继续调用栈内执行,最后线程执行结束,栈空间被回收,局部变量也被回收了。

如此操作,是优化前在堆中,优化后在栈中,从而减少了堆中对象的分配和销毁,从而优化性能。

什么是方法逃逸?

作为调用参数传递到其他方法中,这种称为方法逃逸。

举例说明:

public void test(){// 获取到 getCustomer 方法内部创建的 customerfinal Customer customer = getCustomer();System.out.println(customer.getName());
}public Customer getCustomer() {final Customer customer = new Customer();// 将该对相关传递到方法外部return customer;
}class Customer{String name;
}

什么是线程逃逸?

可能被外部线程访问到,譬如赋值给可以在其他线程中访问的实例变量,这种称为线程逃逸。

举例说明:

public void test() throws ExecutionException, InterruptedException {// 创建一个callable任务(便于获取返回值)final FutureTask<Customer> task = new FutureTask<>((Callable<Customer>) () -> {// ①、子线程创建对象final Customer customer = new Customer();System.out.println("子线程执行,并创建Customer对象: " + customer);// ②、将子线程方法内部创建的对象返回给主线程return customer;});// 执行子线程任务new Thread(task).start();// ③、主线程阻塞获取到子线程创建的对象final Customer result = task.get();// 主线程修改从子线程获取的对象属性result.setName("test");System.out.println("主线程获取到子线程的返回值:" + result);
}class Customer{String name;
}

对象的逃逸程度

        对象由低到高的不同逃逸程度:

从不逃逸 ---> 方法逃逸  --->  线程逃逸

逃逸分析的优化方法

1.栈上分配(Stack Allocations)

在Java虚拟机中,Java堆上分配创建对象的内存空间几乎是Java程序员都知道的常识,Java堆中的对象对于各个线程都是共享和可见的,只要持有这个对象的引用,就可以访问到堆中存储的对象数据。
虚拟机的垃圾收集子系统会回收堆中不再使用的对象,但回收动作无论是标记筛选出可回收对象,还是回收和整理内存,都需要耗费大量资源。
如果确定一个对象不会逃逸出线程之外,那让这个对象在栈上分配内存将会是一个很不错的主意,对象所占用的内存空间就可以随栈帧出栈而销毁。
在一般应用中,完全不会逃逸的局部对象和不会逃逸出线程的对象所占的比例是很大的,如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了, 垃圾收集子系统的压力将会下降很多【优点】。
栈上分配可以支持方法逃逸,但不支持线程逃逸。

2.标量替换(Scalar Replacement)

标量:若一个数据已经无法再分解更小的数据来表示了,Java虚拟机中的原始数据类型(int、long等数值类型及reference类型等)都不能再进一步分解了,那么这些数据就可以被称为标量。

聚合量:相对的,若一个数据可以继续分解,那它就被称为聚合量。Java对象就是典型的聚合量。

标量替换:如果把一个Java对象拆散,根据程序访问的情况,将其用到的成员变量恢复为原始类型来访问,这个过程就称为标量替换。

假如逃逸分析能够证明一个对象不会被方法外部访问,并且这个对象可以被拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。将对象拆分后,除了可以让对象的成员变量在栈上(栈上存储的数据,很大机会被虚拟机分配至物理机器的高速寄存器中存储)分配和读写之外,还可以为后续进一步的优化手段创建条件。

标量替换可以视作栈上分配的一种特例,实现更简单(不用考虑整个对象完整结构的分配),但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内。

举例说明:

public static void main(String[] args){test();
}private static void test(){Position position = new Position(1,2);System.out.println("position.x="+position.x +"; position.y="+position.y);
}class Position{private int x;private int y;
}

position对象的作用域只在test方法中,通过逃逸分析,会被优化为:

public static void main(String[] args){test();
}private static void test(){int x = 1;int y = 2;System.out.println("position.x="+position.x +"; position.y="+position.y);
}

3.同步消除(Synchronization Elimination)[锁消除]

线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个变量不会逃逸出线程,无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,对这个变量实施的同步措施也就可以安全地消除掉。
如果JVM通过逃逸分析,发现一个对象只能从一个线程被访问到,则访问这个对象时,可以不加同步锁。如果程序中使用了synchronized锁,则JVM会将synchronized锁消除。
注意:
这种情况针对的是synchronized锁,而对于Lock锁,则JVM并不能消除。    
要开启同步消除,需要加上-XX:+EliminateLocks参数。因为这个参数依赖逃逸分析,所以同时要打开 -XX:+DoEscapeAnalysis选项。

举例说明:

public void test(){Object obj = new Object();synchronized(obj){System.out.println(obj);}
}
obj对象的作用域只在test方法中,通过逃逸分析,会被优化为:
public void test(){Object obj = new Object();System.out.println(obj);
}

总结

由此看来,并不是所有的对象和数组,都是在堆上进行分配的,由于即时编译的存在,如果JVM发现某些对象没有逃逸出方法,就很有可能被优化成在栈上分配。

逃逸分析设置JVM参数配置

命令参数 备注
-XX:+DoEscapeAnalysis 手动开启逃逸分析
-XX:+PrintEscapeAnalysis 查看分析结果
-XX:+EliminateAllocations 开启标量替换
-XX:+EliminateLocks 开启同步消除
-XX:+PrintEliminateAllocations 查看标量的替换情况

作者:筱白爱学习!!

欢迎关注转发评论点赞沟通,您的支持是筱白的动力!

JVM虚拟机中优化技术之逃逸分析相关推荐

  1. 【java】深入理解Java JVM虚拟机中init和clinit的区别

    1.概述 转载并且补充:深入理解Java JVM虚拟机中init和clinit的区别 原创 之所以来转载一下,是因为,有个学弟问了这个问题 这个知识点,回顾起来应该是我看 深入理解Java JVM虚拟 ...

  2. 防火墙技术在计算机网络安全中的应用分析,计算机网络安全中防火墙技术的应用分析...

    计算机网络安全中防火墙技术的应用分析 张忠霜 湛江科技学院 524000 摘要:在我国计算机技术不断发展和进步的现阶段,这项技术已经广泛应用于各行各业的建设中,为我国居民的生活和工作提供了更多的便利, ...

  3. java在jvm虚拟机中是如何实现多态的?

    原文地址:https://blog.csdn.net/huangrunqing/article/details/51996424 众所周知,多态是面向对象编程语言的重要特性,它允许基类的指针或引用指向 ...

  4. 如果面试官问你 JVM,额外回答逃逸分析技术会让你加分!

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

  5. JVM 逃逸分析 (史上最全)

    对于JVM"逃逸分析" 特性,也是近年来大厂面试.高薪面试的常见面试题. 和逃逸分析有关的常见面试题: Java中的对象一定是在堆上分配的吗? 注:本文以 PDF 持续更新,最新尼 ...

  6. JVM逃逸分析(同步省略、标量替换、栈上分配)

    在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件.第二段编译是把.class转换成机器指令的过程. ...

  7. 深入理解Java中的逃逸分析

    转载自  深入理解Java中的逃逸分析 在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件.第二段编译 ...

  8. 逃逸分析和标量替换技术,你明白了吗

    文章目录 逃逸分析 怎么做逃逸分析后的优化? 什么是线程分配缓冲TLAB? 什么是标量替换? 实战 开始逃逸分析(默认开启的) 关闭逃逸分析-XX:-DoEscapeAnalysis 逃逸分析 逃逸分 ...

  9. Java - 深入理解Java中的逃逸分析

    在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件.第二段编译是把.class转换成机器指令的过程. ...

最新文章

  1. tideways+xhgui搭建php 7的性能测试环境
  2. RTSP流媒体数据传输的两种方式(TCP和UDP)
  3. java 类型 字节_Java数据类型-字节
  4. C指针原理(7)-C内嵌汇编
  5. crv仪表上的i是什么指示灯_汽车打不着火是怎么回事,仪表盘汽车发动机故障灯亮是什么情况故障指示灯图解大全集...
  6. 信息学奥赛C++语言:小玉家的电费
  7. flume介绍与原理
  8. error LNK2019 unresolved external symbol __imp__IoDeleteDevice 错误
  9. 一场由SameSite字段引发的前端悲剧
  10. 2018下半年初级程序员考试时间
  11. 记一次hw中的上线骚姿势(异速联+用友U8)
  12. malloc函数和free函数的使用方法解析
  13. PDF文件怎么修改,PDF文件修改的方法
  14. tp6 的unique验证
  15. Matlab之classification learner app无法从workspace导入label (response variable)
  16. 计算机社团动员大会发言稿,计算机科学与技术学院召开“2020年双创实践线上动员大会”...
  17. 淘宝新店已经不死不活没有生意怎么办
  18. JavaScript实现React实现网页转换成图片截屏下载
  19. 怎样用postman做接口测试,一篇足矣
  20. 分布式系统限流策略/秒杀系统限流设计

热门文章

  1. The Sandbox 与《时代周刊》达成合作,在元宇宙建立“纽约时报广场”
  2. Java分页查询报错 Every derived table must have its own alias
  3. 火狐浏览器图片显示异常,控制台检查是图片A,但是显示的是上一张图片
  4. Time_Wait相关问题
  5. java文件操作getAbsolutePath和getCanonicalPath的区别
  6. 机器视觉之图像基础知识,让计算机去理解获取数字图像与视频中的信息
  7. React开发工具无法访问文件网址
  8. iPhone8接连出现充电等问题,问题扩大将被禁上飞机
  9. 人工智能检测医学图像——子宫肌瘤B超图
  10. 如何用 CSS 修出好看的照片