浅析JVM逃逸的原理及分析

 更新时间:2018年10月09日 11:22:36   投稿:laozhang  
在本篇文章里我们给大家分享了JVM逃逸的原理及分析的相关知识点内容,需要的读者们可以学习下。

我们都知道Java中的对象默认都是分配到堆上,在调用栈中,只保存了对象的指针。当对象不再使用后,需要依靠GC来遍历引用树并回收内存。如果堆中对象数量太多,回收对象还有整理内存,都会会带来时间上的消耗,GC表示压力很大,然后影响性能。所以,在我们日常开发中,内存,时间都是相当的宝贵,该如何优化堆栈开销,是一个比较重要的问题。

在这里,我以逃逸分析角度聊聊JVM优化的那些事儿。

为什么“逃逸”

在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。通俗点讲,如果一个对象的指针被多个方法或者线程引用时,那么我们就称这个对象的指针(或对象)的逃逸(Escape)。

网上有位博友这么形容逃逸,用了一段简单直接的代码,我觉得挺直截了当的,可以供参考:

public StringBuilder escapeDemo1(System a, System b) {
  StringBuilder stringBuilder = new StringBuilder();
  stringBuilder.append(a);
  stringBuilder.append(b);
  return stringBuilder;
}

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

那么,我可以改一下代码:

public String escapeDemo2(System a, System b) {
  StringBuilder stringBuilder = new StringBuilder();
  stringBuilder.append(a);
  stringBuilder.append(b);
  return stringBuilder.toString();
}

如此,就没有返回StringBuilder,而是toString(),那么StringBuilder没有从方法中直接脱离,就没有发生逃逸。

什么是逃逸分析

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

逃逸分析的原理

Java本身的限制(对象只能分配到堆中),我可以这么理解了,为了减少临时对象在堆内分配的数量,我会在一个方法体内定义一个局部变量,并且该变量在方法执行过程中未发生逃逸,按照JVM调优机制,首先会在堆内存创建类的实例,然后将此对象的引用压入调用栈,继续执行,这是JVM优化前的方式。然后,我采用逃逸分析对JVM进行优化。即针对栈的重新分配方式,首先找出未逃逸的变量,将该变量直接存到栈里,无需进入堆,分配完成后,继续调用栈内执行,最后线程执行结束,栈空间被回收,局部变量也被回收了。如此操作,是优化前在堆中,优化后在栈中,从而减少了堆中对象的分配和销毁,从而优化性能。

逃逸的方式

方法逃逸:在一个方法体内,定义一个局部变量,而它可能被外部方法引用,比如作为调用参数传递给方法,或作为对象直接返回。或者,可以理解成对象跳出了方法。

线程逃逸:这个对象被其他线程访问到,比如赋值给了实例变量,并被其他线程访问到了。对象逃出了当前线程。

逃逸分析的好处

如果一个对象不会在方法体内,或线程内发生逃逸(或者说是通过逃逸分析后,使其未能发生逃逸)

1. 栈上分配

一般情况下,不会逃逸的对象所占空间比较大,如果能使用栈上的空间,那么大量的对象将随方法的结束而销毁,减轻了GC压力

2. 同步消除

如果你定义的类的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。

3. 标量替换

Java虚拟机中的原始数据类型(int,long等数值类型以及reference类型等)都不能再进一步分解,它们可以称为标量。相对的,如果一个数据可以继续分解,那它称为聚合量,Java中最典型的聚合量是对象。如果逃逸分析证明一个对象不会被外部访问,并且这个对象是可分解的,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。拆散后的变量便可以被单独分析与优化,可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间了。

开启设置

在JDK 6u23以上是默认开启,这里将设置重新明确一下:

强制开启:  

-server -XX:+DoEscapeAnalysis -XX:+PrintGCDetail -Xmx10m -Xms10m

关闭逃逸分析:

-server -XX:-DoEscapeAnalysis -XX:+PrintGCDetail -Xmx10m -Xms10m

写在结尾

栈上的空间一般而言是非常小的,只能存放若干变化和小的数据结构,无法存储大容量数据。目前的实现都是采用不那么准确但是时间压力相对较小的算法来完成逃逸分析,这就可能导致效果不稳定。所以,逃逸分析的效果只能在特定场景下,满足高频和高数量的小容量的变量分配结构,才是合适的。

相关文章

  • 详解Java中String类的各种用法

    详解Java中String类的各种用法

    Java中定义了String和StringBuffer两个类来封装对字符串的各种操作,存放于java.lang包中,是Java语言的核心类,提供了字符串的比较、查找、截取、大小写转换等操作,无需导入即可直接使用它们。让我们来详细了解它吧
    2021-11-11
  • SpringMVC文件上传 多文件上传实例

    SpringMVC文件上传 多文件上传实例

    这篇文章主要介绍了SpringMVC文件上传 多文件上传实例,有需要的朋友可以参考一下
    2014-01-01
  • Java8 如何正确高效的使用并行流

    Java8 如何正确高效的使用并行流

    这篇文章主要介绍了Java8 如何正确高效的使用并行流,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • SpringBoot整合iText7导出PDF及性能优化方式

    SpringBoot整合iText7导出PDF及性能优化方式

    在SpringBoot项目中整合iText7库以导出PDF文件,不仅能够满足报告生成需求,而且可以处理复杂的文档布局与样式,整合步骤包括添加Maven依赖、编写PDF生成代码,性能优化方面,建议使用流式处理、缓存样式与字体、优化HTML/CSS结构、采用异步处理
    2024-09-09
  • Java基于二维数组实现的数独问题示例

    Java基于二维数组实现的数独问题示例

    这篇文章主要介绍了Java基于二维数组实现的数独问题,涉及java针对数组的遍历、计算、转换等相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • Java实现去除文档阴影的示例代码

    Java实现去除文档阴影的示例代码

    文稿扫描大家用的都比较频繁、想是各种证件、文件都可以通过扫描文稿功能保存到手机。相比直接拍照,在扫描文稿时,程序会对图像进行一些矫正。比如去除阴影、修正倾斜、旋转矫正等。进行这些处理后的图片要更加容易识别。今天就来讨论一下去除阴影的操作
    2022-12-12
  • springboot项目部署到宝塔的详细图文教程

    springboot项目部署到宝塔的详细图文教程

    网上关于宝塔运行springBoot的东西说有点迷糊,但是有一句话很重要,Spring boot项目只需要JDK环境即可部署成功,下面这篇文章主要给大家介绍了关于springboot项目部署到宝塔的详细图文教程,需要的朋友可以参考下
    2023-05-05
  • JVM要双亲委派的原因及如何打破它

    JVM要双亲委派的原因及如何打破它

    平时做业务开发比较少接触类加载器,但是如果想深入学习,了解类加载的原理是必不可少的.java的类加载器有哪些?什么是双亲委派?为什么要双亲委派?如何打破它?接下来本文就带大家详细介绍这些知识 ,需要的朋友可以参考下
    2021-06-06
  • Springboot深入讲解nocos的整合与使用

    Springboot深入讲解nocos的整合与使用

    Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台,在项目开发过程中,我们经常使用nacos作为配置中心和注册中心。本文章我们就从代码层面研究下springboot是如何整合nacos使用的
    2022-07-07
  • XXL-Job端口额外占用问题的解决方法小结

    XXL-Job端口额外占用问题的解决方法小结

    最近博主在Spring整合XXL-JOB到项目时发现了个问题,注册执行器需要额外占用端口,也就是我们每启动一个程序,除了程序本身的API端口外,还需要额外开放一个执行器端口,所以本文给大家分享了XXL-Job端口额外占用问题的解决方法小结,需要的朋友可以参考下
    2024-05-05

最新评论