大家好,我是程序员牛肉。
之前在和朋友聊天的时候,他突然问我什么是“逃逸分析”。说实话当时我还真不太能完整的讲出什么是逃逸分析。这玩意虽然我看八股的时候经常遇见,但之前还真没专项学习过。因此我们今天来完整的介绍一下什么是逃逸分析。
在正式的介绍逃逸分析之前,我们先来看一段代码:
package org.example;
public class Maintest {
public static void main(String[] args) {
while (true) {
Integer integer = new Integer(111111111);
}
}
}
这段代码的逻辑很简单:就是不断的去创建一个值为111111111的integer对象。
[这里考考你们:为什么在创建intege对象的时候,我们的值要是“111111111”这种大数字,而不是随便给个1或者2?]
而这些对象都会被分配到堆中,使得堆内存很快被占满而触发GC。为了让这一过程更加明显,我们需要手动的设置一下JVM参数,使得GC过程被打印到控制台中可视化,以及通过调整堆大小的方式,使得更快的触发GC。对应的JVM参数为:
-Xmx10m -Xms10m -XX:+PrintGC -XX:-DoEscapeAnalysis
-Xmx10m
设置 JVM 堆的最大内存为 10MB。这意味着在 Java 程序运行过程中,JVM 可以使用的最大内存量为 10MB。如果程序试图分配超过这个大小的内存,可能会抛出 OutOfMemoryError
异常。-Xms10m
设置 JVM 堆的初始内存为 10MB。JVM 启动时,堆内存会被初始化为这个大小。通常建议将 -Xms
和 -Xmx
设置为相同的值,以避免在运行时频繁调整堆内存大小,从而提高性能。-XX:+PrintGC
这是一个 JVM 的诊断参数,开启后 JVM 会在进行垃圾回收时将相关信息打印到标准输出。这些信息有助于开发者了解垃圾回收的频率、回收的内存量等,从而对应用程序的内存使用情况进行分析和调优。-XX:-DoEscapeAnalysis
关闭逃逸分析。逃逸分析是 JVM 的一项优化技术,它可以分析对象的作用域,判断对象是否会逃逸出方法或线程。如果对象不会逃逸,JVM 可以对其进行一些优化,如栈上分配、标量替换等。关闭此选项会禁用这些优化。我们可以在下面的视频中观看这段代码的运行结果:
在这段代码的运行过程中,由于我们搭建了一个死循环来不断的创建对象加入到堆内存中,导致JVM的堆内存被快速挤占,频发引发GC。照着这样整,服务就离瘫痪不远了。
因此JDK 官方想到:我们可以分析对象的作用域,对于 像是上述代码中integer
这种仅在 while
循环内部使用,没有被返回给调用者、存储到全局变量或传递到其他线程中的对象,就可以将 integer
对象直接分配在栈上,而不是堆上。
这样一来,当每次循环结束时,该对象所占用的栈空间会随着栈帧的弹出而自动释放,无需等待垃圾回收器来处理,大大减轻了堆内存的压力,也减少了 GC 的频率。
我们可以看一看在开启逃逸分析之后这个代码的运行情况:
[JDK6以后,逃逸分析就默认开启了。因此我们只需要在上面提到的JVM的配置参数中删除之前配置的“-XX:-DoEscapeAnalysis”就可以。]
我们可以看到在开启了逃逸分析之后,我们的integer对象并不会被频繁的创建在堆内存上,而是存储在栈空间上,随着栈帧的弹出而自动释放,无需等待垃圾回收器来处理,大大减轻了堆内存的压力,也减少了 GC 的频率。
其实通过这个实例。我们就能够大致理解什么是“逃逸分析”:
[在 Java 中,对象通常被分配在堆内存中。堆内存由垃圾回收器(GC)管理,但频繁的堆分配和垃圾回收可能会导致性能开销。逃逸分析的目的是通过分析对象的使用范围,判断对象是否需要分配到堆内存中,或者是否可以通过其他方式优化内存分配。]
当一个对象发生逃逸,在栈内存上开始分配空间的时候。JVM还会进行第二次优化:通过标量替换来拆解对象。
[标量替换是 Java 虚拟机(JVM)在逃逸分析基础上的一种优化技术。当逃逸分析确定一个对象不会逃逸出方法时,JVM 会将这个对象分解为其包含的标量成员变量,并将这些标量直接存储在栈帧或寄存器中,而不是在堆上创建对象。这样可以避免在堆上创建对象和垃圾回收的开销。]
这玩意听起来高端,说白了就是不存储对象,而是存储对象的各个字段。这些字段存储在栈上或寄存器中,访问速度更快,因为栈和寄存器的访问速度比堆快,提高了程序的执行效率。
而一个对象的逃逸不仅仅发生在方法层面,也有可能发生在线程层面。
public class Maintest {
public void threadEscape() {
Object object = new Object();
new Thread(() -> {
System.out.println(object); // 对象逃逸到其他线程
}).start();
}
}
public class Maintest {
public void threadEscape() {
Object object = new Object();
synchronized(object)
{
System.out.println(object);
}
}
}
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有