前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Java的对象一定是在堆上分配的嘛?谁这么说就直接用“逃逸分析”反驳他!

Java的对象一定是在堆上分配的嘛?谁这么说就直接用“逃逸分析”反驳他!

作者头像
程序员牛肉
发布于 2025-01-22 01:25:35
发布于 2025-01-22 01:25:35
6800
代码可运行
举报
运行总次数:0
代码可运行

大家好,我是程序员牛肉。

之前在和朋友聊天的时候,他突然问我什么是“逃逸分析”。说实话当时我还真不太能完整的讲出什么是逃逸分析。这玩意虽然我看八股的时候经常遇见,但之前还真没专项学习过。因此我们今天来完整的介绍一下什么是逃逸分析。

在正式的介绍逃逸分析之前,我们先来看一段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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参数为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-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
运行
AI代码解释
复制
public class Maintest {
    public void threadEscape() {
        Object object = new Object();
        new Thread(() -> {
            System.out.println(object); // 对象逃逸到其他线程
        }).start();
    }
}

而如果对象没有发生线程级别的逃逸,那么JVM就会开启同步消除技术,来消除没有必要的锁操作,例如下面的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Maintest {
    public void threadEscape() {
        Object object = new Object();
        synchronized(object)
        {
            System.out.println(object);
        }
    }
}

在这段代码中,JVM会通过逃逸分析来判断这个对象在方法内被创建,且在方法执行期间,该对象不会被其他线程访问到,那么这个对象的同步操作就是多余的,JVM 就可以将其同步机制消除,从而减少不必要的性能开销。

经过上面的介绍,我们可以知道逃逸分析一共有以下三个优点:

那今天关于逃逸分析的文章就介绍到这里了。相信通过我的介绍,你已经大致了解了JVM中的逃逸分析。希望我的文章可以帮到你。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员牛肉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JVM本地方法栈&堆
上一节我们介绍了程序计数器和Java虚拟机栈,今天我们一起了解一下关于本地方法栈和Java堆的相关知识。
shysh95
2020/07/16
6150
JVM本地方法栈&堆
JVM-彻底搞懂 逃逸分析&标量替换
通过上图的对象分配流程,我们可以知道逃逸分析是发生在第一步判断对象是否可以在栈上分配的时候, 在栈上分配的目的是为了减少将对象分配到堆上的概率,节约堆内存,减少GC压力。
小小工匠
2021/08/17
2K0
JVM内存与垃圾回收篇第8章堆
为新对象分配内存是一件非常严谨和复杂的任务,JVM的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收后是否会在内存空间中产生内存碎片。
yuanshuai
2022/08/22
4540
JVM内存与垃圾回收篇第8章堆
JVM简介—1.Java内存区域
Java虚拟机在执行Java程序的过程中,会把它所管理的内存划分为若干个不同的数据区域,这些区域各有各的用途以及各自的创建和销毁时间也不一样。有的区域会随着虚拟机的进程启动而存在,有的区域则依赖用户线程的启动和结束而进而跟着建立和销毁。
东阳马生架构
2025/03/10
390
JVM学习第一天(虚拟机的前世今生与与Java的内存区域)
其实说JVM的时候有很多人会懵, 也很不理解,我会写Java代码就可以了,我干嘛要学这个,其实不是的,学习JVM是很有必要性的;
彼岸舞
2020/09/30
5280
java栈内存初始化,阿里面试官:小伙子,你给我说一下JVM对象创建与内存分配机制吧…
虚拟机遇到一条new指令(new关键字、对象的克隆、对象的序列化等)时,会先去检查这个指令的参数在常量池中定位到一个类的符号引用,并且这个符号引用代表的类是否应被加载过,如果没有那么就去加载该类
全栈程序员站长
2022/08/28
3260
java栈内存初始化,阿里面试官:小伙子,你给我说一下JVM对象创建与内存分配机制吧…
3分钟搞清楚 JVM逃逸分析
作为一个合格java开发者都知道,基本上所有对象都是在堆上创建。但是,这里还是没有把话说绝对哈,指的是基本上所有。
田维常
2023/02/27
3670
3分钟搞清楚 JVM逃逸分析
深入理解JVM和GC
先从自定义的ClassLoader看是否有,没有App中看, 在Extension中看,再Bootstrap中看,都没有,Bootstrap看自己能不能加载,不能就交给Extension加载,也不能就交给App加载,也没有就交给自定义加载器加载。 最后还是没有 抛出异常 ClassNotFound。找到就返回
西柚dzh
2022/06/09
5140
深入理解JVM和GC
Java 11 - 逃逸分析
逃逸分析是一种可以有效减少Java中同步负载和内存堆分配压力的跨函数全局数据流分析方法. 通过逃逸分析, 编译器能够分析出一个新的对象的引用范围, 从而决定是否要将这个对象分配在堆上.
Reck Zhang
2021/08/11
7070
Java 引用逃逸那些事
为了对线上程序的性能进行优化分析, 最近在看广受推荐的《深入理解Java虚拟机》,整本书的内容不少, 目前只是根据自己所需的进行阅读, 在后续读完整本内容配合笔记再写篇博客来记录下.而现在阅读过程中,发现 引用逃逸 和 逃逸分析这个两个概念 并不太了解,还容易混淆,于是就写下这篇博客来帮助下认识 Java 中的 引用逃逸 和 逃逸分析.
闻人的技术博客
2019/09/19
1.7K0
Java 引用逃逸那些事
【GC系列】JVM堆内存分代模型及常见的垃圾回收器
现在大部分用到的垃圾回收器在逻辑上是分代的,除了G1之外的其他垃圾回收器在逻辑上和物理上都是分代的。
行百里er
2020/12/02
1.2K0
【GC系列】JVM堆内存分代模型及常见的垃圾回收器
Java中的四种引用类型
概述 Java.lang.ref 是 Java 类库中比较特殊的一个包,它提供了与 Java垃圾回收器密切相关的引用类。StrongReference(强引用),SoftReference(软引用),WeakReference(弱引用),PhantomReference(虚引用)。这四种引用的强度按照上面的顺序依次减弱. 引用类型对比 序号 引用类型 取得目标对象方式 垃圾回收条件 是否可能内存泄漏 1 强引用 直接调用 不回收 可能 2 软引用 通过 get()方法 视内存情况回收 不可能 3 弱引用 通
java404
2018/05/18
7510
【原创】JVM系列04 | 栈上分配
当面试官问你对象都分配哪里,你把 JVM 内存结构介绍一下然后说分配在堆上,没啥问题,给你打 8 分。如果你还能聊一聊栈上分配,一定是加分项,我想面试官会考虑给你 10 分。
java进阶架构师
2020/05/29
4770
性能优化|JVM内存分配机制
了解jvm的同学应该都知道,堆其实是分为新老年代的,这主要是为了进行垃圾回收而设计的一种结构
AI码师
2020/11/19
5200
性能优化|JVM内存分配机制
再清楚不过了,JVM逃逸分析,你一定得知道
提到JVM,相信大家一定知道JVM是什么?但是,提到逃逸分析,相信大多数人都可能一脸懵逼,逃逸分析到底是什么呢?接下来给大家分享一下。
Java程序猿阿谷
2020/12/16
2.3K0
再清楚不过了,JVM逃逸分析,你一定得知道
JVM之堆
约定:新生区 <–> 新生代 <–> 年轻代 、 养老区 <–> 老年区 <–> 老年代、 永久区 <–> 永久代
冬天vs不冷
2025/01/20
990
JVM之堆
原创|面试官:Java对象一定分配在堆上吗?
最近在看 Java 虚拟机方面的资料,以备工作中的不时之需。首先我先抛出一个我自己想的面试题,然后再引出后面要介绍的知识点如逃逸分析、标量替换、栈上分配等知识点
每天晒白牙
2020/08/21
1.4K0
原创|面试官:Java对象一定分配在堆上吗?
JVM内存分配机制之栈上分配与TLAB的区别
在java开发中,我们普遍认知中,new出的对象是直接分配到堆空间中,而实际情况并非如此,其实大家伙可以思考一下,无论方法的生命周期长与短,只要new的对象就存放在堆中,那么这样只会对jvm的gc产生一个比较大的负担 而前几天在看到jvm调优书中有说到,new出来的对象并非所有都存在堆内存中,其实还有其他另外两个地方可以进行存储new出的对象,称之为栈上分配和TLAB
黎明大大
2020/11/24
2.4K0
JVM内存分配机制之栈上分配与TLAB的区别
Java JVM 面试题
阿彬学java
2025/01/09
1030
JVM堆
堆针对一个JVM进程来说是唯一的,也就是一个进程只有一个JVM,但是进程包含多个线程,他们是共享同一堆空间的。
麋鹿大哥
2020/08/19
3850
相关推荐
JVM本地方法栈&堆
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文