美文网首页
对象创建的两段论

对象创建的两段论

作者: 自天佑之吉无不利 | 来源:发表于2023-09-18 20:54 被阅读0次

需要特别注意的一个重要理念,就是 Java 的语言层和 JVM 层是两个层级。认识到这一点会对理解 JVM 以及日常优化系统起到非常大的帮助。在对象创建这里也是这样。

一个 Java 对象的创建是由 JVM 层和 Java 语言层共同协作完成的。JVM 就像一个开发商一样负责拿到图纸申请空间,也就是类信息,然后照着 Java 对象协议来盖房,最后把变量初始化到默认值再给到你。这个时候创建对象在 JVM 层就已经完成了,这个 Java 对象已经有了运行时空间的地址,可以对它进行操作了。但是对于 Java 语言层来说,还是希望给它做一些定制化的装饰,比如把墙面刷成粉色再与大家见面,这就是下面的对象初始化阶段。

image.png

对象创建快捷流水线

严格意义上来说,第二条流水线并没有生产出任何 Java 语言层意义上的对象,而是回归到创建对象要解决的问题本身去解决问题。这就像马斯克提到的第一性原理:回归本质,然后重构。之所以这样做,还是离不开时代的影响。

当 Java 刚刚崛起的时候,作为面向对象语言,Java 的那句“everything is object”,就像标签一样深深烙印在每个人的心中,也烙印在 Java 的身上。但就是这句给 Java 带来无限荣耀的 Slogan,如今却成为了 Java 低效的代名词。相比于其他新出世的编程语言,人们开始觉得如果解决任何问题都要先创建对象,而创建对象又要经历这么复杂的流程的话,成本似乎太高了。

本着与其被别人革命,不如自己先革自己命的原则,从 JDK 1.8 开始,JVM 搭建起它的第二条对象创建的流水线,或者说从对象创建要解决的问题出发,绕过了之前的对象创建环节,直接在 JVM 层面去解决问题。

这条全新的流水线主要由四个节点构成。


image.png

对象逃逸分析

第一个节点就是对象逃逸分析,它是以方法为边界的,如果一个对象的使用超出当前方法,就叫做对象逃逸,而我们优化的目标是防止对象逃逸。
JVM 中关于逃逸分析的相关参数主要有三个。

  • 开启逃逸分析:-XX:+DoEscapeAnalysis
  • 关闭逃逸分析:-XX:-DoEscapeAnalysis
  • 显示分析结果:-XX:+PrintEscapeAnalysis

标量替换

经过上面的优化和逃逸分析,我们可以确认方法内的局部变量对象未发生逃逸,也就是说方法内的这个对象的使用没有超过该方法,同时也不会超出当前线程。这种情况下,JVM 实际创建这个对象的时候,可以不在堆上,而是改为直接创建这个方法所使用的成员变量来直接替代。

我们可以通过 -XX:+EliminateAllocations 启动标量替换,通过 -XX:+PrintEliminateAllocations 查看标量替换情况。

栈上分配

栈上分配指方法中声明的变量和创建的对象,直接从当前线程所使用的栈上分配空间,而不在堆上创建对象。HotSpot 的栈上分配实际指的就是上面提到的标量替换。栈上分配不仅更加快速而且避免了对创建对象的 GC,一举多得。这也是 JVM 在标准对象生产流水线以外再创建这条快捷流水线的原因。

同步消除

当一个对象经过了逃逸分析、标量替换以及栈上分配后,说明这个对象不会逃逸出当前线程。因此针对这个对象的所有同步措施都可以被删除掉,避免了加锁、去锁等一系列耗时的操作。

public String  getBookName(){
    Book book = new Book("Book");
    synchronized (book){
        book.name = "Book1";
    }
    return  book.name;
}

上面的代码经过JVM优化后就变成了下面的代码:

public char[] EscapeExample() {
    char[] charArray = {'B', 'o', 'o','k'};
    charArray = {'B', 'o', 'o','k','1'};
    return charArray
}

对象创建标准流水线 VS 对象创建快捷流水线

虽然介绍了对象创建快捷流水线的种种优势,但是快捷流水线并不是标准流水线的完全替代品,两者有各自的优势,也有各自的应用场景,快捷流水线适合生命周期短且不会跨线程访问的小对象,而对于生命周期长,需要跨线程访问等的对象,我们依然需要依赖标准流水线。只是相对以往单一的选择,JVM 为我们提供了更丰富的功能,让我们在性能优化时更加得心应手。

此文章为9月Day17学习笔记,内容来源于极客时间《云时代JVM实战 》,强烈推荐该课程

相关文章

  • 谈ObjC对象的两段构造模式

    两段构造模式是指创建对象需要先调用alloc方法或allocWithZone方法(已废弃),再调用init方法或i...

  • js里 for of 和 for in的区别分析

    for of 不能遍历对象,for in 可以 对比两段对象的遍历代码 for in 运行结果为: for of ...

  • javascript面向对象解析(一)

    创建对象的方式 json对象方式创建对象 Object方式 声明构造函数方式,创建对象 -- 这种创建对象的方式用...

  • JS笔记-006-JS对象-数字-字符串-日期-数组-逻辑

    JS对象 创建 JavaScript 对象 通过 JavaScript,您能够定义并创建自己的对象。 创建新对象有...

  • 创建对象只是开辟个堆内存那么简单吗?

    java对象 对象的创建 java的对象是在运行时创建的,创建对象的的触发条件有以下几种: 用new语句创建对象,...

  • 对象的创建

    对象的创建 虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检...

  • 对象的创建

    一、创建过程 二、重点说明 1.在Java堆中为对象分配内存空间 分配方法 有两种主流方法:指针碰撞;空闲列表。 ...

  • 对象的创建

    创建对象1.工厂模式 2.构造函数模式 3.原型模式 4.组合使用原型模式和构造函数模式 (常见的方式) 5.动态...

  • 对象的创建

    工厂模式 优点:避免创建多个对象时的重复代码问题 缺点:无法解决对象识别问题(不知道一个对象的类型) 构造函数模式...

  • 对象的创建

    1.给对象分配内存 两种方式:1.指针碰撞 2.空闲列表 2.线程安全性问题 3.初始化对象 4.执行构造方法

网友评论

      本文标题:对象创建的两段论

      本文链接:https://www.haomeiwen.com/subject/khagvdtx.html