Java的对象一定是在堆上分配的嘛?谁这么说就直接用“逃逸分析”反驳他!
Java的对象一定是在堆上分配的嘛?谁这么说就直接用“逃逸分析”反驳他!
大家好,我是程序员牛肉。
之前在和朋友聊天的时候,他突然问我什么是“逃逸分析”。说实话当时我还真不太能完整的讲出什么是逃逸分析。这玩意虽然我看八股的时候经常遇见,但之前还真没专项学习过。因此我们今天来完整的介绍一下什么是逃逸分析。
在正式的介绍逃逸分析之前,我们先来看一段代码:
代码语言:javascript代码运行次数:0运行复制package ;
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参数为:
代码语言:javascript代码运行次数:0运行复制-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 会将这个对象分解为其包含的标量成员变量,并将这些标量直接存储在栈帧或寄存器中,而不是在堆上创建对象。这样可以避免在堆上创建对象和垃圾回收的开销。]
这玩意听起来高端,说白了就是不存储对象,而是存储对象的各个字段。这些字段存储在栈上或寄存器中,访问速度更快,因为栈和寄存器的访问速度比堆快,提高了程序的执行效率。
而一个对象的逃逸不仅仅发生在方法层面,也有可能发生在线程层面。
比如下面这个代码中就发生了线程级别的逃逸:
代码语言:javascript代码运行次数:0运行复制public class Maintest {
public void threadEscape() {
Object object = new Object();
new Thread(() -> {
println(object); // 对象逃逸到其他线程
}).start();
}
}
而如果对象没有发生线程级别的逃逸,那么JVM就会开启同步消除技术,来消除没有必要的锁操作,例如下面的代码:
代码语言:javascript代码运行次数:0运行复制public class Maintest {
public void threadEscape() {
Object object = new Object();
synchronized(object)
{
println(object);
}
}
}
在这段代码中,JVM会通过逃逸分析来判断这个对象在方法内被创建,且在方法执行期间,该对象不会被其他线程访问到,那么这个对象的同步操作就是多余的,JVM 就可以将其同步机制消除,从而减少不必要的性能开销。
经过上面的介绍,我们可以知道逃逸分析一共有以下三个优点:
那今天关于逃逸分析的文章就介绍到这里了。相信通过我的介绍,你已经大致了解了JVM中的逃逸分析。希望我的文章可以帮到你。
本文参与 腾讯云自媒体同步曝光计划,分享自。原始发表:2025-01-20,如有侵权请联系 cloudcommunity@tencent 删除内存线程java对象垃圾回收#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
上一篇:搞懂Go泛型,看这一篇就够了
推荐阅读
留言与评论(共有 16 条评论) |
本站网友 丹吉布森 | 2分钟前 发表 |
JVM 可以使用的最大内存量为 10MB | |
本站网友 闰年怎么算 | 21分钟前 发表 |
-Xms10m 设置 JVM 堆的初始内存为 10MB | |
本站网友 青年餐厅 | 7分钟前 发表 |
这些信息有助于开发者了解垃圾回收的频率 | |
本站网友 世界三大高峰 | 20分钟前 发表 |
在栈内存上开始分配空间的时候 | |
本站网友 除疤痕 | 19分钟前 发表 |
这意味着在 Java 程序运行过程中 | |
本站网友 美达 | 6分钟前 发表 |
]当一个对象发生逃逸 | |
本站网友 肾部保养手法视频 | 22分钟前 发表 |
而不是堆上 | |
本站网友 trinity | 30分钟前 发表 |
大大减轻了堆内存的压力 | |
本站网友 中医妇科医生 | 17分钟前 发表 |
+PrintGC 这是一个 JVM 的诊断参数 | |
本站网友 孕妇上火了怎么办 | 4分钟前 发表 |
回收的内存量等 | |
本站网友 股票大跌 | 10分钟前 发表 |
使得堆内存很快被占满而触发GC | |
本站网友 长沙化妆学校 | 6分钟前 发表 |
无需等待垃圾回收器来处理 | |
本站网友 闵行二手房信息 | 29分钟前 发表 |
也减少了 GC 的频率 | |
本站网友 最快最有效减肥方法 | 7分钟前 发表 |
大大减轻了堆内存的压力 | |
本站网友 cgv星星影城 | 14分钟前 发表 |
+PrintGC 这是一个 JVM 的诊断参数 |