Java中的对象一定是在堆上分配的吗?

对象一定分配在堆中吗?有没有了解逃逸分析技术?

2 个回答

  • 曹德
    用顺其自然态度过随遇而安的生活.
    ### 对象一定分配在堆中吗? 不一定。JVM 通过「逃逸分析」技术,可以将逃不出方法的对象分配在栈上。 ### 什么是逃逸分析? 逃逸分析 (Escape Analysis) 是一种跨函数全局数据流分析算法,用于减少 Java 程序中的同步负载和内存堆分配压力。Java Hotspot 编译器通过逃逸分析,能够确定一个新对象的引用使用范围,从而决定是否将该对象分配到堆上。 逃逸分析的基本原理是分析指针的动态范围,与编译器优化的指针分析和外形分析相关联。当一个变量(或对象)在方法中分配后,如果其指针可能被返回或被全局引用,就可能被其他方法或线程引用,这种现象称为指针(或引用)的逃逸。 ### 逃逸分析的好处 1. **栈上分配**: - 对象分配在栈上可以降低垃圾收集器的运行频率。 2. **同步消除**: - 如果某个对象只能被一个线程访问,对该对象的操作可以不需要同步,从而消除同步开销。 3. **标量替换**: - 逃逸分析可以将对象分解成多个基本类型,并将其分配在栈上,而不是堆上。这样可以: - 减少内存使用,因为不需要生成对象头。 - 提高程序内存回收效率,减少 GC 频率。
    编辑于 2023-01-21 20:07 •IP属地 北京
    举报
    评论
    收藏

  • 吴敏
    你爱你的玫瑰是因为你在你的玫瑰上花了太多的时间!
    Java中的对象不一定是在堆上分配的,因为JVM通过逃逸分析,能够分析出一个新对象的使用范围,并以此确定是否要将这个对象分配到堆上。 这里,我们接触了一个新名词:逃逸分析。相信很多小伙伴不是很明白,那我们继续往下看。 ### 逃逸分析 逃逸分析的概念 先以官方的形式来说下什么是逃逸分析。逃逸分析就是:一种确定指针动态范围的静态分析,它可以分析在程序的哪些地方可以访问到指针。 在JVM的即时编译语境下,逃逸分析将判断新建的对象是否逃逸。即时编译判断对象是否逃逸的依据:一种是对象是否被存入堆中(静态字段或者堆中对象的实例字段),另一种就是对象是否被传入未知代码。 直接说这些概念,确实有点晕啊,那我们就来两个示例。 ### 对象逃逸示例 一种典型的对象逃逸就是:对象被复制给成员变量或者静态变量,可能被外部使用,此时变量就发生了逃逸。 我们可以用下面的代码来表示这个现象。 ```java /** * @author binghe * @description 对象逃逸示例1 */ public class ObjectEscape{ private User user; public void init(){ user = new User(); } } ``` 在ObjectEscape类中,存在一个成员变量user,我们在init()方法中,创建了一个User类的对象,并将其赋值给成员变量user。此时,对象被复制给了成员变量,可能被外部使用,此时的变量就发生了逃逸。 另一种典型的场景就是:对象通过return语句返回。如果对象通过return语句返回了,此时的程序并不能确定这个对象后续会不会被使用,外部的线程可以访问到这个变量,此时对象也发生了逃逸。 我们可以用下面的代码来表示这个现象。 ```java /** * @author binghe * @description 对象逃逸示例2 */ public class ObjectReturn{ public User createUser(){ User user = new User(); return user; } } ``` 给出两个示例,相信小伙伴们对JVM的逃逸分析多少有点了解了吧,没错,JVM通过逃逸分析,能够分析出新对象的使用范围,从而决定新对象是否要在堆上进行分配。 ### 逃逸分析的优点 逃逸分析的优点总体上来说可以分为三个:对象可能分配在栈上、分离对象或标量替换、消除同步锁。我们可以使用下图来表示。 ![image.png](https://images.codewd.com/f82901de03115679d80e1da01d3144db.png) 对象可能分配在栈上 JVM通过逃逸分析,分析出新对象的使用范围,就可能将对象在栈上进行分配。栈分配可以快速地在栈帧上创建和销毁对象,不用再将对象分配到堆空间,可以有效地减少 JVM 垃圾回收的压力。 分离对象或标量替换 当JVM通过逃逸分析,确定要将对象分配到栈上时,即时编译可以将对象打散,将对象替换为一个个很小的局部变量,我们将这个打散的过程叫做标量替换。将对象替换为一个个局部变量后,就可以非常方便的在栈上进行分配了。 同步锁消除 如果JVM通过逃逸分析,发现一个对象只能从一个线程被访问到,则访问这个对象时,可以不加同步锁。如果程序中使用了synchronized锁,则JVM会将synchronized锁消除。 这里,需要注意的是:这种情况针对的是synchronized锁,而对于Lock锁,则JVM并不能消除。 要开启同步消除,需要加上 -XX:+EliminateLocks 参数。因为这个参数依赖逃逸分析,所以同时要打开 -XX:+DoEscapeAnalysis 选项。 所以,并不是所有的对象和数组,都是在堆上进行分配的,由于即时编译的存在,如果JVM发现某些对象没有逃逸出方法,就很有可能被优化成在栈上分配。
    发布于 2022-12-18 19:49 •IP属地 江西
    举报
    评论
    收藏2