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
      相关文章
      |
      2月前
      |
      存储 Java
      【编程基础知识】 分析学生成绩:用Java二维数组存储与输出
      本文介绍如何使用Java二维数组存储和处理多个学生的各科成绩,包括成绩的输入、存储及格式化输出,适合初学者实践Java基础知识。
      94 1
      |
      3月前
      |
      缓存 JavaScript Java
      常见java OOM异常分析排查思路分析
      Java虚拟机(JVM)遇到内存不足时会抛出OutOfMemoryError(OOM)异常。常见OOM情况包括:1) **Java堆空间不足**:大量对象未被及时回收或内存泄漏;2) **线程栈空间不足**:递归过深或大量线程创建;3) **方法区溢出**:类信息过多,如CGLib代理类生成过多;4) **本机内存不足**:JNI调用消耗大量内存;5) **GC造成的内存不足**:频繁GC但效果不佳。解决方法包括调整JVM参数(如-Xmx、-Xss)、优化代码及使用高效垃圾回收器。
      180 15
      常见java OOM异常分析排查思路分析
      |
      3天前
      |
      缓存 算法 搜索推荐
      Java中的算法优化与复杂度分析
      在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
      16 6
      |
      22天前
      |
      存储 监控 算法
      深入探索Java虚拟机(JVM)的内存管理机制
      本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
      |
      27天前
      |
      监控 算法 Java
      jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
      【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
      |
      1月前
      |
      安全 Java 编译器
      Java对象一定分配在堆上吗?
      本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
      Java对象一定分配在堆上吗?
      |
      2月前
      |
      缓存 算法 Java
      JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
      这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
      89 4
      JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
      |
      1月前
      |
      存储 Java 关系型数据库
      在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
      在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
      69 2
      |
      1月前
      |
      Java 关系型数据库 数据库
      面向对象设计原则在Java中的实现与案例分析
      【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
      38 2
      |
      2月前
      |
      存储 算法 Java
      Java虚拟机(JVM)的内存管理与性能优化
      本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。