new出来的对象,不一定在堆上?聊聊Java虚拟机的优化技术:逃逸分析

简介: 逃逸分析是一种静态程序分析技术,用于判断对象的可见性与生命周期。它帮助即时编译器优化内存使用、降低同步开销。根据对象是否逃逸出方法或线程,分析结果分为未逃逸、方法逃逸和线程逃逸三种。基于分析结果,编译器可进行同步锁消除、标量替换和栈上分配等优化,从而提升程序性能。尽管逃逸分析计算复杂度较高,但其在热点代码中的应用为Java虚拟机带来了显著的优化效果。

逃逸分析(Escape Analysis)是一种静态程序分析技术,主要用于判定对象的可见范围(Visibility)与生命周期(Lifetime)。该技术是现代即时编译器实现局部化优化、提升内存使用效率、降低同步成本的基础。
通俗来说,逃逸分析的核心在于回答这样一个问题:某个对象是否可能“逃逸”出它所创建的方法或线程作用域?
逃逸分析的结果通常分为三种情形。
1)未逃逸(No Escape):对象完全局限在当前方法内,既未作为返回值,也未传递到其他线程或方法。
2)方法逃逸(Method Escape):对象作为参数传递到其他方法中,虽然不一定跨线程访问,但由于编译器无法确定外部方法的副作用,因此仍视为潜在逃逸。
3)线程逃逸(Thread Escape):对象的引用被赋值给共享变量,或作为任务传递给其他线程。这类对象无法进行逃逸相关优化,必须保留其线程安全保障。
下面代码的是对象未逃逸的例子:

// add方法中创建了一个名为NonEscapeObject的对象。
// 这个对象仅在add方法中使用,用于计算两个整数的和。
// 这个对象没有作为方法的返回值、赋值给全局变量或作为参数传递给其他方法。
// 因此它被认为是未逃逸的。
int add(int a, int b) {
   

  NonEscapeObject o = new NonEscapeObject(a, b);

  return obj.getX() + obj.getY();
}

class NonEscapeObject {
   

    private int x;

    private int y;
}

基于逃逸分析的信息,即时编译器可以执行一些优化,例如同步锁消除(Synchronization Elimination)、标量替换(Scalar Replacement)和栈上分配(Stack allcotion)。

同步锁消除
线程同步是一个相对耗时的过程,如果逃逸分析能确定一个共享变量不会逃出线程,无法被其他线程访问,那这个共享变量的读写肯定就不会有竞争,对这个变量实施的同步措施也就可以消除掉。

// 由于obj没有逃逸出doSomething()方法的范围,编译器可以进行逃逸分析并确定该对象不会被其他线程访问。
// 在逃逸分析确定obj对象不会逃逸的情况下,编译器可以消除对该对象的同步锁操作。
void doSomething() {
   

    Object obj = new Object();

    synchronized (obj) {
   

        // 对obj进行一些操作

        // ...
    }
}

标量替换
标量(scalar)是指一个无法再分解成更小的数据的数据。Java 中的基本数据类型就是标量。相对的Java 中的对象就是聚合量(Aggregate),因为它可以分解成其他聚合量和标量。
如果经过逃逸分析,发现一个对象并没有逃逸出方法和线程,那么就可以将这个对象视为一组标量值。这样,Java虚拟机就可以将这个对象的所有字段视为局部变量,从而在栈上分配这些局部变量,而不是在堆上分配整个对象,这样可以减少堆内存的占用。

void test() {
   

   Point point = new Point(1,2);

   System.out.println("point.x" + point.x + ";point.y" + point.y);
}

class Point {
   

    private int x;

    private int y;
}

假设有一个Point对象,包含x和y两个字段。如果经过逃逸分析,发现这个Point对象并没有逃逸出方法,那么Java虚拟机就可以将这个Point对象视为两个独立的标量值x和y,然后在栈上分配这两个值,而不是在堆上分配整个Point对象。

void test() {
   
    int x = 1;

    int y = 2;

    System.out.println("point.x = " + x + "; point.y=" + y);
}

栈上分配
Java的对象是在堆上分配的,Java虚拟机对堆内存的垃圾对象回收是一个耗时的过程。在一般应用中,不会逃逸的局部对象所占的比例很大,如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁,垃圾收集系统的压力将会小很多。
虽然逃逸分析理论上支持将非逃逸对象直接分配到栈上,从而避免堆内存开销与垃圾回收成本,但如HotSpot虚拟机并未真正实现物理意义上的栈上分配。原因在于:在支持线程抢占、嵌套调用、异常恢复与栈帧迁移(如逃逸到堆)等复杂运行时语义的情况下,栈上对象生命周期管理的正确性将变得异常困难,容易引发并发可见性等问题。因此,Hotspot虚拟机并没有进行实际的栈上分配,而是使用了标量替换这一技术。
尽管逃逸分析为即时编译器带来了多种激进优化的可能,但它本身也是一项计算复杂度较高的静态分析技术。在分析过程中,编译器需要对对象的引用路径进行全程追踪,判断其是否会被其他线程访问、是否会通过方法返回或赋值跨出当前作用域。特别是在存在复杂控制流、间接调用或反射的情况下,分析准确性与代价都将急剧上升。
这种计算成本并非微不足道:在代码编译时长与运行时性能收益之间,并不总是呈现出正向关系。在某些边缘场景中,逃逸分析所带来的优化甚至可能因分析开销过大、代码形态不佳(例如过度拆箱、短生命周期对象)而无法收回性能投入。因此,Java虚拟机会采用热点代码触发机制,仅对高频路径进行逃逸分析,以期在收益与成本之间实现动态平衡。
虽然这项技术并不十分成熟,但是它也是即时编译器优化技术中一个十分重要的手段。

未完待续

很高兴与你相遇!如果你喜欢本文内容,记得关注哦!

目录
相关文章
|
2月前
|
监控 Cloud Native Java
Quarkus 云原生Java框架技术详解与实践指南
本文档全面介绍 Quarkus 框架的核心概念、架构特性和实践应用。作为新一代的云原生 Java 框架,Quarkus 旨在为 OpenJDK HotSpot 和 GraalVM 量身定制,显著提升 Java 在容器化环境中的运行效率。本文将深入探讨其响应式编程模型、原生编译能力、扩展机制以及与微服务架构的深度集成,帮助开发者构建高效、轻量的云原生应用。
255 44
|
2月前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
295 1
|
3月前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
688 1
|
3月前
|
数据采集 搜索推荐 Java
Java 大视界 -- Java 大数据在智能教育虚拟学习环境构建与用户体验优化中的应用(221)
本文探讨 Java 大数据在智能教育虚拟学习环境中的应用,涵盖多源数据采集、个性化推荐、实时互动优化等核心技术,结合实际案例分析其在提升学习体验与教学质量中的成效,并展望未来发展方向与技术挑战。
|
30天前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
96 8
|
28天前
|
存储 Java Go
【Java】(3)8种基本数据类型的分析、数据类型转换规则、转义字符的列举
牢记类型转换规则在脑海中将编译和运行两个阶段分开,这是两个不同的阶段,不要弄混!
160 2
|
28天前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
169 1
|
2月前
|
Java Spring
如何优化Java异步任务的性能?
本文介绍了Java中四种异步任务实现方式:基础Thread、线程池、CompletableFuture及虚拟线程。涵盖多场景代码示例,展示从简单异步到复杂流程编排的演进,适用于不同版本与业务需求,助你掌握高效并发编程实践。(239字)
181 6
|
2月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
3月前
|
Java 测试技术 API
2025 年 Java 开发者必知的最新技术实操指南全览
本指南涵盖Java 21+核心实操,详解虚拟线程、Spring Boot 3.3+GraalVM、Jakarta EE 10+MicroProfile 6微服务开发,并提供现代Java开发最佳实践,助力开发者高效构建高性能应用。
500 4