探究跨代引用和逃逸分析如何提升程序性能!

简介: 探究跨代引用和逃逸分析如何提升程序性能!


🍊 跨代引用

在Java虚拟机中,跨代引用指的是老年代中的对象引用了年轻代中的对象。当进行年轻代的垃圾回收时,需要扫描老年代中所有引用年轻代的对象,在老年代里面做大量的遍历和扫描操作,这是非常消耗性能的。为了优化这个问题,JVM引入了记忆集(Card Table)的抽象数据结构。

记忆集记录了从非收集区域指向收集区域的一个指针集合,这个指针集合是由一组连续的内存卡片(Card)组成的。每个内存卡片的大小是2^18个字节,可以理解为一个内存页面。记忆集能够记录指向年轻代的内存卡片中所有的指针,将这些指针标记为"dirty"。在minor gc时,只需要扫描"dirty"的内存卡片,而不是扫描老年代中所有引用年轻代的对象。

思路比较清晰,现在来看一下这个问题的示例代码:

public class CrossGenerationReferenceExample {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        Object obj = new Object();
        for (int i = 0; i < 1000000; i++) {
            list.add(obj);
        }
    }
}

这个示例代码中的问题在于,对象obj在年轻代中创建,但是被放到了老年代中的ArrayList里面。这个时候,当进行年轻代的垃圾回收时,就需要扫描老年代中所有引用年轻代的对象,而且这个ArrayList还是一个非常大的对象,扫描的效率非常低下。

现在来看一下使用记忆集优化之后的代码:

public class CrossGenerationReferenceExample {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        Object obj = new Object();
        for (int i = 0; i < 1000000; i++) {
            list.add(obj);
            if (i % 20000 == 0) {
                System.gc();
            }
        }
    }
}

在这个示例代码中,每添加20000个对象就手动执行一次垃圾回收操作。这个操作会强制触发记忆集的更新,即将内存卡片中的指针标记为"dirty"。这样,在下一次垃圾回收时,就只需要扫描"dirty"的内存卡片,提高了垃圾回收的效率。

🍊 逃逸分析

逃逸分析是Java虚拟机优化的重要手段之一,它可以通过静态分析来确定对象的动态作用域,判断一个对象是否会“逃逸”,即被其他方法或线程引用。如果对象不会逃逸,则可以将对象的创建和销毁都放在栈上进行,从而减少在堆上进行内存分配和回收的开销。

逃逸分析有三种程度:从不逃逸、方法逃逸和线程逃逸。这三个由低到高表示不同逃逸的程度。

🎉 栈上分配

在Java虚拟机中,堆中的对象对于所有线程都是可见的,只要持有这个对象的引用,就可以访问到堆中存储的对象数据。虚拟机的垃圾回收子系统会回收堆中不再使用的对象,但是回收动作无论是标记筛选出可回收对象,还是回收和整理内存,都需要耗费大量资源。

如果确定一个对象不会逃逸出线程之外,那让这个对象在栈上分配内存将会是一个很不错的主意,对象所占用的内存空间就可以随栈帧出栈而销毁。在一般应用中,完全不会逃逸的局部对象和不会逃逸出线程的对象所占的比例是很大的,如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁,垃圾回收子系统的压力将会下降很多。

栈上分配可以支持方法逃逸,但不能支持线程逃逸。

下面是一个栈上分配的示例代码:

public static void main(String[] args) {
    for (int i = 0; i < 1000000; i++) {
        Point p = new Point(1, 2);
        p.setX(i);
        p.setY(i);
    }
}
class Point {
    private int x;
    private int y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public void setX(int x) {
        this.x = x;
    }
    public void setY(int y) {
        this.y = y;
    }
}

在这个示例代码中,每次循环都创建一个Point对象,在Point对象的构造函数中将xy初始化。Point对象不会逃逸出方法之外,因此可以将它们分配在栈上。

🎉 标量替换

一个数据已经无法再分解成更小的数据来表示了,Java虚拟机中的原始数据类型(int、long等数值类型及reference类型等)都不能再进一步分解了,那么这些数据就可以被称为标量。

一个数据可以继续分解,那它就被称为聚合量。Java中的对象就是典型的聚合量。如果把一个Java对象拆散,根据程序访问的情况,将其用到的成员变量恢复为原始类型来访问,这个过程就称为标量替换。

假如逃逸分析能够证明一个对象不会被方法外部访问,并且这个对象可以被拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。

将对象拆分后,除了可以让对象的成员变量在栈上(栈上存储的数据,很大机会被虚拟机分配至物理机器的高速寄存器中存储)分配和读写之外,还可以为后续进一步的优化手段创建条件。标量替换可以视作栈上分配的一种特例,实现更简单(不用考虑整个对象完整结构的分配),但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内。

下面是一个标量替换的示例代码:

public static void main(String[] args) {
    for (int i = 0; i < 1000000; i++) {
        String s = new String("abc" + i);
        int length = s.length();
    }
}

在这个示例代码中,每次循环都创建一个String对象,并取出它的长度。逃逸分析能够证明String对象不会逃逸出方法之外,因此可以将对象的成员变量拆分为一个char数组和一个offset整型变量。在Java虚拟机内部,会把char数组和offset整型变量分别分配在栈上,而不需要在堆上分配String对象。

总之,逃逸分析是一种非常重要的JVM优化手段,能够让JVM能够更好地利用现代计算机的硬件资源,提高程序的性能。通过栈上分配和标量替换等技术,能够在一定程度上减少对堆内存的使用,从而让垃圾回收的效率更高,进而让程序能够更好地使用计算机的内存和CPU资源。


相关文章
|
程序员 Shell 数据格式
python股票量化交易(1)---K线图、均线与成交量绘制
python股票量化交易(1)---K线图、均线与成交量绘制
2574 0
python股票量化交易(1)---K线图、均线与成交量绘制
|
7月前
|
监控 负载均衡 JavaScript
有哪些有效的方法可以优化Node.js应用的性能?
有哪些有效的方法可以优化Node.js应用的性能?
358 69
|
6月前
|
NoSQL Java 微服务
2025 年最新 Java 面试从基础到微服务实战指南全解析
《Java面试实战指南:高并发与微服务架构解析》 本文针对Java开发者提供2025版面试技术要点,涵盖高并发电商系统设计、微服务架构实现及性能优化方案。核心内容包括:1)基于Spring Cloud和云原生技术的系统架构设计;2)JWT认证、Seata分布式事务等核心模块代码实现;3)数据库查询优化与高并发处理方案,响应时间从500ms优化至80ms;4)微服务调用可靠性保障方案。文章通过实战案例展现Java最新技术栈(Java 17/Spring Boot 3.2)的应用.
474 9
|
6月前
|
消息中间件 监控 关系型数据库
覆盖迁移工具选型、增量同步策略与数据一致性校验
本文深入解析数据迁移核心挑战,涵盖工具选型、增量同步优化与一致性校验三大关键环节,结合实战案例与代码方案,助开发者规避风险,实现高效可靠迁移。
251 0
|
存储 监控 安全
JVM工作原理与实战(四十):ZGC原理
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了ZGC、ZGC核心技术、ZGC的内存划分、ZGC的执行流程、分代ZGC的设计等内容。
883 1
|
12月前
|
人工智能 自然语言处理 监控
《AI赋能共享经济:资源配置与服务质量的双重优化》
共享经济借助互联网平台实现闲置资源高效利用,AI技术的融入进一步优化资源配置和服务质量。AI通过精准需求预测、智能调度和动态分配策略提升资源使用效率;借助个性化推荐、智能客服和实时监控改善用户体验。典型案例如Airbnb和滴滴出行展示了AI在提高预订率、减少等待时间和提升安全方面的显著成效。尽管面临数据隐私等挑战,AI仍为共享经济带来巨大创新和发展机遇。
528 18
|
机器学习/深度学习 人工智能 自然语言处理
【机器学习】贝叶斯算法在机器学习中的应用与实例分析
【机器学习】贝叶斯算法在机器学习中的应用与实例分析
1219 1
|
JavaScript Java 测试技术
基于SpringBoot+Vue的大学生兼职平台的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的大学生兼职平台的详细设计和实现(源码+lw+部署文档+讲解等)
255 3
|
前端开发 网络协议 Go
为什么ChatGPT选择了SSE,而不是WebSocket?
为什么ChatGPT选择了SSE,而不是WebSocket?
837 2
|
数据库 Docker 容器
Docker版TDengine2.6升级到Tdengine3.0
请注意,这些步骤提供了一般的升级指南。具体的升级过程可能因你的环境和配置而异。在进行升级之前,建议查阅TDengine官方文档和Docker镜像的相关文档,以获取更详细的升级和配置信息。
396 5

热门文章

最新文章