我们都知道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中jvm逃逸问题分析,包括了java中jvm逃逸问题分析的使用技巧和注意事项,需要的朋友参考一下 引言: 逃逸分析(Escape Analysis)是众多JVM技术中的一个使用不多的技术点,本文将通过一个实例来分析其使用场景。 概念 逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够
本文向大家介绍面试中遇到的java逃逸分析问题,包括了面试中遇到的java逃逸分析问题的使用技巧和注意事项,需要的朋友参考一下 前言 记得几年前有一次栈长去面试,问到了这么一个问题: Java中的对象都是在堆中分配吗?说明为什么! 当时我被问得一脸蒙逼,瞬间被秒杀得体无完肤,当时我压根就不知道他在考什么知识点,难道对象不是在堆中分配吗?最后就没然后了,回去等通知了。。 下面我收集了一下网友的回答。
本文向大家介绍浅析Ajax的 原理及优缺点,包括了浅析Ajax的 原理及优缺点的使用技巧和注意事项,需要的朋友参考一下 1、ajax技术的背景 不可否认,ajax技术的流行得益于google的大力推广,正是由于google earth、google suggest以及gmail等对ajax技术的广泛应用,催生了ajax的流行。而这也让微软感到无比的尴尬,因为早在97年,微软便已经发明了ajax中的
我正在使用terraform文件中的remote exec provisioner在ec2上运行一些命令。但我一直在逃避命令中的特殊字符。这部分代码来自我的main。远程exec provisioner部分中的tf文件。terraform中出现的错误是“无效字符”和“无效多行字符串”。我想要正确的字符串序列,以便这些命令可以在我的ec2上执行。
我已通过设置以下内容将PowerShell添加到上下文菜单: 这工作得很好,除了我出色地命名为的文件夹。它失败了: 从文档: 指定位置的路径。LiteralPath参数的值的使用方式与键入的完全相同。没有字符被解释为通配符。如果路径包含转义字符,请用单引号将其括起来。单引号告诉PowerShell不要将任何字符解释为转义序列。 我是否可以选择使用,希望我的目录名称中没有可能的通配符?或者可以在中转
本文向大家介绍java DelayQueue的原理浅析,包括了java DelayQueue的原理浅析的使用技巧和注意事项,需要的朋友参考一下 在对DelayQueue延迟功能的使用上,很多人不能后完全理解延迟的一些功能使用,这里我们深入来挖掘一下DelayQueue的原理。 下面将从构造方法、接口、继承体系三个方面进行分析,需要注意的是,相较于其它的阻塞队列,DelayQueue因为延迟的功能多