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

对象创建的两段论

作者: 自天佑之吉无不利 | 来源:发表于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实战 》,强烈推荐该课程

    相关文章

      网友评论

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

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