JVM逃逸分析

逃逸分析

堆是分配对象存储的唯一选择吗?
  • 在深入了解java虚拟机中关于Java堆内存有这样一段描述:随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配,标量替换优化技术将会导致一些微妙变化,所有的对象都分配到堆上也渐渐变得不那么"绝对"了
  • 在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普通的常识,但是,有一种特殊情况,那就是如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配,这样就无需再堆上分配内存,也无须进行垃圾回收了,这也是最常见的堆外存储技术
  • 此外,基于OpenJDK深度定制的TaoBaoVM,其中创新的GCIH(GC Invisible heap)技术实现off-heap,将生命周期较长的Java对象从heap中移至heap外,并且GC不能管理GCIH内部的对象,以此达到降低GC的回收频率和提升GC的回收效率的目的
逃逸分析
  • 如何将堆上的对象分配到栈,需要使用逃逸分析手段
  • 这是一种可用有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法
  • 通过逃逸分析,Java HotSpot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上
  • 逃逸分析的基本行为是分析对象动态作用域
    • 当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸
    • 当一个对象在方法被定义后,它被外部方法所引用,则认为发生逃逸,例如作为参数传递到其他方法中,或者作为返回值返回.
  • 在JDK 6u23版本之后,HotSpot中默认已经开启了逃逸分析
基于逃逸分析进行代码优化
  • 栈上分配:JIT编译器在编译期间根据逃逸分析结果,如果一个对象没有发生逃逸,那么就可能优化成栈上分配

  • 同步省略:如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步

    • 在动态编译同步块的时候,JIT编译器可以借助逃逸分析来判断同步代码块所使用的锁对象是否只能被一个线程访问而没有发布到其他线程,如果没有,那么JIT编译器在编译这个 同步块的时候就会取消对部分代码的同步,这样就能大大提高并发和性能,这个取消同步的过程就叫同步省略,也叫锁消除
  • 分离对象或标量替换,有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在cpu的寄存器中

    • 标量(Scalar)是指一个无法再分解成更小更小数据的数据,Java中的原始数据类型就是标量
    • 相对的,那些可以分解的数据叫做聚合量(Aggregate),java中的对象就是聚合量,因为它可以分解成其他聚合量和标量
    • 在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过JIT优化,就会把这个对象拆解成若干个其中包含的若干个成员变量来代替,这个过程就是标量替换
逃逸分析并不成熟
  • 关于逃逸分析的论文在1999年就已经发布了,但直到JDK1.6才实现,而且这项技术到如今也并不是十分成熟的
  • 其根本原因就是无法保证逃逸分析的性能消耗一定低于垃圾回收的消耗,虽然经过逃逸分析可以做标量替换,栈上分配和锁消除,但是逃逸分析自身也是需要进行一些列复杂的分析的,这其实也是一个相对耗时的过程
  • 一个极端的例子,就是经过逃逸分析之后,发现没有一个对象是不逃逸的,那这个逃逸分析的过程就白白浪费掉了
  • 虽然这项技术并不十分成熟,但是它也是即时编译器优化中一个十分重要的手段
  • JVM会在栈上分配那些不会逃逸的对象,这在理论上是可行的,但是取决于JVM设计者的选择,HotSpot中其实并未这么做,所以可以明确所有的对象实例都是创建在堆上
### JVM 逃逸分析概述 JVM中的逃逸分析是一种编译期优化技术,用于判断对象是否会逃逸出当前线程或方法的作用域。通过这种分析,JDK能够做出一些优化决策来提高性能,比如栈上分配、同步消除等[^4]。 ### 逃逸分析引发内存泄漏的原因 当对象未正确处理其生命周期时,在某些情况下即使启用了逃逸分析也可能间接造成内存泄漏: - **错误的对象共享**:如果程序逻辑设计不当,使得原本应该局部化的对象意外地被其他部分持有引用,则这些对象无法及时回收,从而形成潜在的内存泄露风险。 - **误判为不逃逸的情况**:对于复杂的数据结构操作,尤其是涉及多线程环境下的并发访问模式下,可能存在难以精确判定是否发生逃逸的情形。此时可能会导致不必要的堆内存在长时间得不到释放而累积成内存泄漏现象。 ### 解决方案 针对上述由逃逸分析可能带来的问题,可以从以下几个方面着手解决: #### 合理调整GC策略与参数配置 适当调节垃圾收集器的选择及其相关参数可以帮助更好地管理应用程序内的资源消耗情况。例如启用G1 GC并合理设定初始和最大堆大小(-Xms,-Xmx),这有助于维持稳定的运行状态,防止由于频繁Full GC造成的性能瓶颈以及由此产生的隐含内存泄漏隐患[^1]。 ```bash java -XX:+UseG1GC -Xms512m -Xmx4g MyApplication ``` #### 审查代码逻辑确保无异常引用保留 仔细审查源码中涉及到对象创建及传递的部分,特别是那些跨作用范围使用的实例变量或者静态成员字段。确认它们不会无意间延长目标对象的生命周朗,进而阻碍正常的垃圾回收过程。 #### 利用工具辅助诊断排查 借助专业的性能剖析工具如VisualVM, MAT(Memory Analyzer Tool)等对正在执行的应用进程实施监控跟踪,定位具体引起内存占用过高的区域,并据此采取针对性措施加以改进。 #### 正确理解并运用逃逸分析特性 虽然现代版本Java已经默认开启此功能,但在特定场景下调优仍有必要深入了解该机制的工作原理。必要时可通过显式指定`-XX:-DoEscapeAnalysis`关闭它来进行对比测试,观察实际效果变化后再做决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

climb.xu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值