Java虚拟机-逃逸分析(Escape Analysis)和栈上分配

简介: Java虚拟机-逃逸分析(Escape Analysis)和栈上分配

 image.gif编辑

 

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

逃逸分析(Escape Analysis)和栈上分配是对JVM进行优化的常规手段,本文主要深入了解一下。

一、概念

1.逃逸

当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。通俗点讲,如果一个对象的指针被多个方法或者线程引用时,那么我们就称这个对象的指针(或对象)的逃逸(Escape)。

具体而言,分为方法逃逸和线程逃逸两种。

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

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

2.栈上分配

主要是指在Java程序的执行过程中,在方法体中声明的变量以及创建的对象,将直接从该线程所使用的栈中分配空间。 就是把没发生逃逸的对象,在栈上分配空间。一般而言,创建对象都是从堆中来分配的,这些对象是有可能发生逃逸的。

3.逃逸分析

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

在JDK 6之后支持对象的栈上分析和逃逸分析,在JDK 7中完全支持栈上分配对象。 其是否打开逃逸分析依赖于以下JVM的设置:

#强制开启:    
-server -XX:+DoEscapeAnalysis -XX:+PrintGCDetail -Xmx10m -Xms10m
#关闭逃逸分析:    
-server -XX:-DoEscapeAnalysis -XX:+PrintGCDetail -Xmx10m -Xms10m

image.gif

二、为什么需要逃逸分析和栈上分配?

为了GC性能。GC主要回收的对象是堆和方法区。GC不会对栈、程序计数器这些进行回收的,因为没东西可以回收。

如果方法逃逸,那么对象就会分配在堆中,这个时候,GC就要工作了。如果没发生方法逃逸,那么对象就分配在栈中,当方法结束后,资源就自动释放了,GC压根不用操心。所以方法逃逸就是为GC来服务的。GC不运行的时候,程序的性能肯定会好点,不会占用程序运行的时间。虽然GC清扫垃圾的速度很快,但是当一个程序足够大的时候,对象就自然多了,垃圾也自然多了,这个时候GC就忙了。

而进行逃逸分析,就是把那些不会发生逃逸的对象直接分配在栈中,这样不需要GC参与资源的释放,性能自然就会有提升了。

三、 逃逸分析和栈上分配的优劣势分析

1.优势

同步消除:线程同步的代价是相当高的,同步的后果是降低并发性和性能。逃逸分析可以判断出某个对象是否始终只被一个线程访问,如果只被一个线程访问,那么对该对象的同步操作就可以转化成没有同步保护的操作,这样就能大大提高并发程度和性能。

标量替换:逃逸分析方法如果发现对象的内存存储结构不需要连续进行的话,就可以将对象的部分甚至全部都保存在CPU寄存器内,这样能大大提高访问速度。

2.劣势

栈上分配受限于栈的空间大小,一般自我迭代类的需求以及大的对象空间需求操作,将导致栈的内存溢出;故只适用于一定范围之内的内存范围请求。

四、参数配置一览

逃逸分析在JDK6U23以上都是默认开启的,开启后会极大地提升性能。

-XX:+DoEscapeAnalysis开启逃逸分析(JDK 6u23以上默认开启)
-XX:-DoEscapeAnalysis 关闭逃逸分析
#标量替换基于分析逃逸基础之上,开启标量替换必须开启逃逸分析
-XX:+EliminateAllocations开启标量替换(jdk1.8默认开启,其它版本未测试)
-XX:-EliminateAllocations 关闭标量替换
#锁消除基于分析逃逸基础之上,开启锁消除必须开启逃逸分析
-XX:+EliminateLocks开启锁消除(jdk1.8默认开启,其它版本未测试)
-XX:-EliminateLocks 关闭锁消除

image.gif

五、总结

    1. 能在方法内创建对象,就不要再方法外创建对象。毕竟这是为了GC好,也是为了提高性能。
    2. 栈上分配可以提升代码性能,降低在多线程情况下的锁使用,但是会受限于其空间的大小。
    3. 逃逸分析的效果只能在特定场景下,满足高频和高数量的小容量的变量分配结构,才是合适的。

    参考资料:

      1. https://www.jianshu.com/p/3ecc626ce304
      2. JVM的栈上分配与逃逸分析(Escape Analysis)_bladestone的博客-CSDN博客
      3. JVM的逃逸分析 - 阿两君 - 博客园
      4. https://www.jianshu.com/p/04fcd0ea5af7
      相关文章
      |
      24天前
      |
      缓存 JavaScript Java
      常见java OOM异常分析排查思路分析
      Java虚拟机(JVM)遇到内存不足时会抛出OutOfMemoryError(OOM)异常。常见OOM情况包括:1) **Java堆空间不足**:大量对象未被及时回收或内存泄漏;2) **线程栈空间不足**:递归过深或大量线程创建;3) **方法区溢出**:类信息过多,如CGLib代理类生成过多;4) **本机内存不足**:JNI调用消耗大量内存;5) **GC造成的内存不足**:频繁GC但效果不佳。解决方法包括调整JVM参数(如-Xmx、-Xss)、优化代码及使用高效垃圾回收器。
      95 15
      常见java OOM异常分析排查思路分析
      |
      2月前
      |
      存储 算法 Java
      惊!Java程序员必看:JVM调优揭秘,堆溢出、栈溢出如何巧妙化解?
      【8月更文挑战第29天】在Java领域,JVM是代码运行的基础,但需适当调优以发挥最佳性能。本文探讨了JVM中常见的堆溢出和栈溢出问题及其解决方法。堆溢出发生在堆空间不足时,可通过增加堆空间、优化代码及释放对象解决;栈溢出则因递归调用过深或线程过多引起,调整栈大小、优化算法和使用线程池可有效应对。通过合理配置和调优JVM,可确保Java应用稳定高效运行。
      113 4
      |
      2月前
      |
      Java 程序员
      Java 一个 Scanner.nextInt 造成的奇怪问题及分析解决过程
      Java 一个 Scanner.nextInt 造成的奇怪问题及分析解决过程
      |
      29天前
      |
      缓存 JavaScript Java
      常见java OOM异常分析排查思路分析
      Java虚拟机(JVM)遇到 OutOfMemoryError(OOM)表示内存资源不足。常见OOM情况包括:1) **Java堆空间不足**:内存被大量对象占用且未及时回收,或内存泄漏;解决方法包括调整JVM堆内存大小、优化代码及修复内存泄漏。2) **线程栈空间不足**:单线程栈帧过大或频繁创建线程;可通过优化代码或调整-Xss参数解决。3) **方法区溢出**:运行时生成大量类导致方法区满载;需调整元空间大小或优化类加载机制。4) **本机内存不足**:JNI调用或内存泄漏引起;需检查并优化本机代码。5) **GC造成的内存不足**:频繁GC但效果不佳;需优化JVM参数、代码及垃圾回收器
      常见java OOM异常分析排查思路分析
      |
      12天前
      |
      Java
      JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
      本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
      |
      6天前
      |
      存储 算法 Java
      深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
      本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
      18 0
      深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
      |
      2月前
      |
      Java 索引
      java中的栈(利用数组实现栈)
      这篇文章通过Java代码示例介绍了如何使用数组实现栈操作,包括栈的初始化、入栈、出栈、判断栈满和空以及遍历栈的方法。
      java中的栈(利用数组实现栈)
      |
      2月前
      |
      Dubbo Java 关系型数据库
      Java SPI机制分析
      文章深入分析了Java SPI机制,以JDBC为例,详细探讨了服务提供者接口的发现、加载过程,并提供了一个序列化服务的实战示例,展示了如何使用ServiceLoader进行服务发现和扩展。
      19 3
      |
      2月前
      |
      监控 算法 安全
      Java并发编程案例分析:死锁的检测与解决
      Java并发编程案例分析:死锁的检测与解决
      25 2
      |
      2月前
      |
      安全 Java API
      精通 Java 后台开发:案例分析与实践
      精通 Java 后台开发:案例分析与实践
      37 2
      下一篇
      无影云桌面