JVM调优之逃逸分析

逃逸分析(Escapse Analysis)是目前虚拟机中比较前沿的优化技术。之所以称为优化技术,他并不是一种直接优化代码的手段,而是穿插与其他优化技术之中,为其他优化技术提供分析技术

1.概述

在讨论逃逸分析之前,我们先举一个生活中的例子——交通肇事逃逸。说道这个词,大家就熟悉多了,机动车驾驶员在发生交通事故的同时,擅自逃离事故现场,使交通事故所引起的民事、刑事、行政责任无法确定,其目的在于推卸、逃脱责任的行为。也就是说,逃逸就是擅自离开规定的范围。而对于Java对象的逃逸分析也是一样,他主要分析对象动态的作用域(这个作用域一般指的是方法体内的作用域)。按照我们正常的思维,一个对象在一个方法总被创建,那么当方法消亡,他也会随着一起消亡。然而,作为一个有经验的开发人员可知,当对象的引用通过调用参数或者返回参数这条途径,会成功逃出方法体本身,进而被其他方法变量引用,这样就成功的逃离了作案现场,我们称之为方法逃逸。甚至还有可能被外部线程访问到,我们最常见的就是静态变量(类变量)赋值,我们称之为线程逃逸

大家想一下,如果一个方法内的对象没有发生逃逸,我们可不可以在方法被销毁的时候直接把对象销毁掉,这样变可以大大减轻垃圾收集器的压力,缩短了“stop the world”,进而提高的性能,增强了用户体验。

2.优化方式

2.1 栈上分配(Stack Allocation)

在Java虚拟机中,对象是创建在Java堆中,这是每一位有经验Java开发人员在熟悉不过的常识。Java堆中的对象是对于各个线程都是共享和可见的,主要持有这个对象的引用,就可以轻松访问存储在对象的数据。虚拟机的垃圾收集器也可以回收不再使用的对象,但是回收动作无论是筛选可回收对象,还是回收和整理内存都需要消耗时间和性能。如果确定一个对象不会逃逸出方法之外,那让这个对象在栈上分配内存将是一个大胆而又很不错的注意。大家都知道,JVM虚拟机中80-90%的对象都是朝生夕死的,如果对象所占用的内存空间随着栈帧出栈而销毁,那垃圾收集器的压力就会大大减小。

2.2 同步消除(Synchronization Elimination)

对于多线程的应用而言,我们通过同步代码块实现线程的安全。然而线程同步代码块也是一个相当耗费性能的过程。如果通过逃逸分析确定这个对象不会逃逸出线程,即无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,这样,我们就可以大胆的把同步线程代码块消除。

2.3 标量替换(Scalar Replacement)

如果我问一下什么是对象,大家一定会想到封装继承多态。然而大家想过没有,所谓的对象,也可以理解成最终由一个或多个原始数据类型(int,long等)组成。这里的对象可以认为是聚合量,原始数据类型就是所谓的标量。如果逃逸分析证明一个对象不会被外部访问,并且这个对象呗拆散的话,那程序真正执行的时候将可能不创建这个对象,而改为直接创建他的若干个呗这个方法使用到的成员变量带代替。将对象拆分后,除了可以让对象的成员变量在栈上分配和读写之外,还可以为后续进一步的优化手段创建条件。

3.权衡分析

逃逸分析是一个相当复杂的过程,那么逃逸分析所要消耗的性能与逃逸分析多带来的性能提升究竟哪个大,还是一个未知事项。如果经过逃逸分析后发现没有几个不逃逸的对象,那简直就是得不偿失了。所以java虚拟机只能对于时间压力相对较小的算法来完成逃逸分析。

4.参数设置

手动开启逃逸分析:-XX:+PrintEscapeAnaysis

开启标量替换:-XX:EliminateAllocations

开启同步消除:+XX:+EliminateLocks

标量替换情况:-XX:+PrintEliminateAllocations

扫描二维码关注公众号,回复: 9585016 查看本文章

 

发布了72 篇原创文章 · 获赞 24 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/oYinHeZhiGuang/article/details/103413081