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
      相关文章
      |
      3月前
      |
      存储 算法 Java
      惊!Java程序员必看:JVM调优揭秘,堆溢出、栈溢出如何巧妙化解?
      【8月更文挑战第29天】在Java领域,JVM是代码运行的基础,但需适当调优以发挥最佳性能。本文探讨了JVM中常见的堆溢出和栈溢出问题及其解决方法。堆溢出发生在堆空间不足时,可通过增加堆空间、优化代码及释放对象解决;栈溢出则因递归调用过深或线程过多引起,调整栈大小、优化算法和使用线程池可有效应对。通过合理配置和调优JVM,可确保Java应用稳定高效运行。
      140 4
      |
      21天前
      |
      安全 Java 编译器
      Java对象一定分配在堆上吗?
      本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
      Java对象一定分配在堆上吗?
      |
      1月前
      |
      缓存 算法 Java
      JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
      这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
      65 4
      JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
      |
      24天前
      |
      存储 算法 Java
      🧠Java零基础 - Java栈(Stack)详解
      【10月更文挑战第17天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
      17 2
      |
      26天前
      |
      存储 算法 Java
      Java虚拟机(JVM)的内存管理与性能优化
      本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
      |
      1月前
      |
      存储 Java
      JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
      这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
      53 0
      JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
      |
      1月前
      |
      存储 安全 Java
      【用Java学习数据结构系列】探索栈和队列的无尽秘密
      【用Java学习数据结构系列】探索栈和队列的无尽秘密
      30 2
      |
      2月前
      |
      存储 算法 Java
      深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
      本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
      112 0
      深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
      |
      3月前
      |
      Java 索引
      java中的栈(利用数组实现栈)
      这篇文章通过Java代码示例介绍了如何使用数组实现栈操作,包括栈的初始化、入栈、出栈、判断栈满和空以及遍历栈的方法。
      java中的栈(利用数组实现栈)
      |
      3月前
      |
      Java
      Java常见JVM虚拟机指令(47个)
      Java常见JVM虚拟机指令(47个)
      69 3
      Java常见JVM虚拟机指令(47个)