zoukankan      html  css  js  c++  java
  • JVM学习笔记二_对象的创建、布局和定位

    对象的创建

    首先,检查new指令的参数是否能在常量池中定位到一个类的符号引用,并且检查该符号引用所代表的类是否已经被加载、解析、初始化过。如果没有,需要 执行相应的类加载过程。

    其次,虚拟机为新生对象分配内存。对象所需的内存大小在类加载完毕后便可以确定。

    然后,虚拟机需要将分配的内存空间都初始化为零值(不包含对象头),保证了对象实例字段在Java代码中可以不赋值就直接使用。

    接下来,虚拟机对对象进行必要的设置,比如:该对象是哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。存在在对象头中。

    虚拟机视角,该对象已经创建完毕。

    一般来说,执行new指令之后,会紧接着执行<init>方法,把对象按照写作者的意愿进行初始化,这样一个对象才完全产生出来。

    另附:

    一、如何分配内存

      1. “指针碰撞”(Bump the Pointer):假设Java堆中是绝对规整的(用过的内存在一边 空闲的内存在一边 中间放一个指针作为分界点的指示器),分配内存就是把指针向空闲空间方向移动一块与对象大小相等的距离。

      2. “空闲列表”(Free List):如果Java堆中不是规整的,已使用的和空闲的相互交错,虚拟机就必须维护一个列表,来记录哪些内存块是可用的,在分配的时候在列表中找一块足够大的空间划分给对象实例,并且更新表上的记录。

    二、并发情况下如何在堆上为对象分配内存

      1. 对内存分配空间的动作进行同步处理(虚拟机采用CAS配失败重试的方式来保证更新的原子性)

      2. 把内存分配的动作按照线程划分在不同的空间之中进行。也就是每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。哪个线程需要分配内存,就在哪个线程的TLAB上分配,只有

      在TLAB用完并分配新的TLAB时,才需要同步锁定。虚拟机是否使用TLAB,可通过参数 -XX:+/-UseTLAB 参数来设定。

    对象的内存布局

    在HotSpot虚拟机中,对象在内存中的布局结构可以分为三块区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。

    一、对象头:包含两部分,一部分是“Mark Word”,存储对象自身运行时的数据。另一部分是类型指针,对象指向它的类元数据的指针,虚拟机通过这个指针确定这个对象是哪个类的实例。

      对象头在32位虚拟机里占64bit 64位虚拟机里占128bit。(1字节=8bit)

      1. Mark Word:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

      这部分数据在32位和64位虚拟机中长度分别为32bit和64bit。

      下面以64bit为例:

    偏向锁标识位 锁标识位 锁状态 存储内容
    0 01 未锁定 unused(25)hashcode(31)unused(1)age(4)
    1 01 偏向锁 JavaThread(54)epoch(2)unused(1)age(4)
    00 轻量级锁 指向栈中锁记录的指针(62)
    10 重量级锁 指向重量级锁的指针(62)
    11 GC标记

      2. 类型指针:并不是所有的虚拟机实现都必须在对象数据上保留类型指针,也就是说,查找对象的元数据信息并不一定要经过对象本身。

    二、实例数据部分

      记录了不管是从父类继承下来的还是子类中定义的各种类型的字段内容。这部分字段的存储顺序会受到 虚拟机分配策略参数字段在Java代码中定义的顺序的影响。HotSpot虚拟机默认的分配策略

      为:long/double,int,short/char,byte/boolean,oop。满足上面条件下,父类定义的变量会出现在子类的变量之前。如果CompactFields参数值为true(默认为true),那么子类的较窄的变量也可能会插

      入到父类变量的空隙中。

    三、对齐填充

      不是必然存在的。HotSpot VM要求对象的起始地址必须为8字节的整数倍,也就是说,对象的大小 必须为8字节的整数倍。所以,当对象实例没有对齐时,需要通过对齐填充来补全。

    对象的访问定位

    有两种方式:

    一、 使用句柄方式:

      Java堆中会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址。句柄中包含了 对象实例数据 和 对象类型数据 的地址。

      优势:reference存储的是稳定的句柄地址,在对象被移动时,只会改变句柄中的 对象实例数据指针,refer本身不用改变。

    二、 使用直接指针访问:

      reference中存储的是对象实例数据的实际地址。需要考虑如何 放置和访问 对象类型数据。

      优势:快,因为节省了一次指针定位的开销。

    ps:对象实例数据 在 Java堆的实例池,对象类型数据 在 方法区。

  • 相关阅读:
    php冒泡排序和快速排序
    在thinkphp中js文件添加路径
    cookiesession
    搭建nginx环境(参考腾讯云实验室)
    验证码快速刷新
    使用Word发送,测试一下
    c++ DLL和c#之间传递字符串
    如何使CEF支持Flash
    如何在Windows上从源码编译Chromium (CEF3) 加入mp3支持
    C#在Linux+Mono环境中使用微信支付证书
  • 原文地址:https://www.cnblogs.com/kawin/p/9986546.html
Copyright © 2011-2022 走看看