美文网首页
new出对象时, JVM内部发生了什么

new出对象时, JVM内部发生了什么

作者: 迷路嘚光芒 | 来源:发表于2020-02-15 23:28 被阅读0次

    看到这篇文章觉得比以前看过的所有文章说的比透彻。 从来不写博客的我, 想写一篇翻译来传播原作者的精神哈哈。

    原文出处:https://www.javaspring.net/java/what-happened-when-new-an-object-in-jvm

    前言

    如你所知, Java时一门面向对象语言, 开发时常常会创建一摞子对象。

    User user = new User();

    这样一行代码, JVM会做些什么事情呢?

    对【对象】的理解

    1. 内存布局

    在Hotspot虚拟机内部, “对象”的内存布局被划分了三部分: 对象头实例数据和填充对齐
    对象头包括两部分信息, 第一部分用来存储对象本身运行时相关数据(HashCode,GC分代年龄,lock锁状态标志,等等)。
    第二部分是类型指针, 指向Class元数据区。虚拟机用这个指针来确定这个对象对应的Class对象(如果有句柄池,就不会有这个东西)。如果new出来的对象是数组,会被存储数组长度,如下所示

    image

    Mard Word是一个【非固定】的把很多信息存储在一个尽可能小空间内的内存区域,同时也会根据对象状态来审时度势。

    ![image](https://img.haomeiwen.com/i2898220/98df423c62544493.jpeg)
    

    实例数据部分是实际存储的有效信息,即代码中定义的各种类型的字段内容,是由父类继承还是在子类中继承(这句是借助翻译工具翻译的, 刚开始没读懂什么意思)。

    “对象填充“并非是实际分配来存储信息的,它只是在虚拟机中扮演一个花瓶的角色,因为HotSpot虚拟机对象对象的起始地址必须是8个字节的整数倍,

    2.【对象】访问

    在Java程序中, 我们通过指针(指向对象的引用)来操作对象。众所周知,对象是存储在堆中,然而其指针是存储在“虚拟机栈“ 那么指针是什么定位到堆内存的对象呢?

    • 直接指针方法(HotSpot实现):直接存储在引用中的地址是堆中对象的地址。优点是定位速度快,缺点是对象移动(GC移动时的对象移动)本身需要修改
    • 句柄方法:Java堆的一部分用作句柄池。引用存储对象的句柄地址,句柄包含对象实例和类型的特定位置信息。优点是对象的移动只改变了句柄中的实例数据指针,缺点是两次定位。

    创建对象的操作

    • 当虚拟机有遇到一个新的指令, 虚拟机会根据指令参数能否在常量池定位到Class的符号引号, 其后检查这个类是否已经被类加载器加载过。如果没有加载, 先加载类。
    • 类型检查通过后,虚拟机将会为新的对象分配内存,所需内存大小在类加载后决定。
    • 内存分配完成后,虚拟机将会把对象初始化为0, 是为了那些在程序代码里未设置初始值的字段能够直接被使用。对于静态变量, 会在类的准备阶段被初始化为0。
    • 设置对象头的基本信息, 类元数据, HashCode, gc年龄,等等
    • 以上操作完成后,一个新的对象诞生了。但是成员方法还未被访问,所有字段都是0。与此同时,要调用构造函数来根据程序员意愿实例化对象。静态变量的初始化操作会在类加载的初始化阶段完成。

    有两种方法来分配内存

    • 规则的Java堆内存(垃圾回收算法用标记压缩), 用一个指针指向空闲内存,内存分配多少,指针移动多少。
    • 不规则的Java堆内存(垃圾回收算法用标记清除),虚拟机维护了一个内存空闲列表, 当内存被分配能从空闲列表找到哪块空闲时足够用的, 同时更新这个列表
    • 当内存不够用时, 将会发生一次GC(垃圾回收)

    分配内存的并发解决方案
    同步分配内存的骚操作---使用“CAS失败重试”来确保更新操作的原子性。每个线程在堆中预先分配一小部分内存,称为线程本地分配缓冲区(TLAB),只有在TLAB耗尽并分配一个新的TLAB时,线程才在其TLAB上分配内存。需要同步锁。由-XX:+/-UseTLAB参数设置

    创建对象重排序问题

    A a = new A();

    它被简单的分解为三个步骤

    1. 为对象分配内存
    2. 初始化对象
    3. 设置指向内存

    在2 , 3步骤, 者2个指令有可能会被重排, 从而引起 在多线程环境下 “在访问对象的时候, 对象还未初始化完成“。 单例模式的双检锁会存在这个问题。你可以用“volatile“来禁止重排序, 问题解决。

    自己写了个代码, 就当是复习了

    file

    如图所示, antlr=new AntlrDemo(); 在这一步的三个“jvm“指令当时, 如果对象的赋值先与初始化, 那么就空指针了。在volatile修饰下, 三个指令不会被重排。

    本文由博客一文多发平台 OpenWrite 发布!

    相关文章

      网友评论

          本文标题:new出对象时, JVM内部发生了什么

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