JVM对象的创建访问定位

对象的创建

  1. Object object = new Object();
  2. 虚拟机接收new指令
  3. 虚拟机根据new的参数在常量池中定位一个类的符号引用
  • 常量池:用于存储编译器所生产字面量和符号引用量(图片来自于wangbiao007博主的博客方法区和常量池)
    常量池
  1. 如果没有找到这个符号引用,说明类还没有被加载,则进行类的加载,解析和初始化。
  2. 类加载检查后,虚拟机将为对象分配内存(位于堆中)。分配内存有下面两种方式
  • 指针碰撞:堆内存中的空闲空间如果十分的规整,使用与未使用的空间全部为连续,只需移动一下指针就可以了
  • 空闲列表:针对堆内存中的空间零散的存在,虚拟机维护着一个列表,记录着哪里被分配了,哪里还空闲。,分配内存的时候,只需要从表中查找。
  1. 内存分配完后,虚拟机将分配的内存初始化为零值(不包括对象头),初始化零值是保证了对象的实例字段在Java代码中不赋初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值。这里顺便说下Java对象的结构。
    对象由对象头(Header)、实例数据(InstanceData)和对齐填充(Padding),3部分组成。
  2. 最后调用对象的<init>方法

对象的结构(对象的内存模型)

  1. 对象头
  • 标志信息:用来存放对象一些固有属性的状态,这些属性从对象创建就有,而不是 Java 的使用者定义的:哈希码(对象的唯一标识)、对象的分代年龄(雨垃圾回收有关)、线程持有的锁、锁的状态、偏向线程 ID、偏向时间戳、数组长度(如果该对象是数组,会有数组长度信息)。
  • 元信息指针:指向方法区中类元信息的指针。
  1. 实例数据
  • 实例的数据信息存放的是一些对 Java 使用者真正有效的信息,也就是类中定义的各个字段,其中还包括从父类继承的字段。
  1. 对齐填充
  • 对其填充这段内存段存在与否取决于前面两部分的长度,为了保证对象内存模型的长度为 8 字节的整数倍,这也是虚拟机自动内存管理的要求。

对象的访问定位

       虚拟机栈是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
       局部变量表存放了编译期可知的各种基本数据类型(long,boolean,int,byte,short,long,double,float)、returnAddress类型(指向了一条字节码指令的地址)和对象引用(reference类型,它不代表对象本身,可能是一个指向对象起始地址的引用指针,也可能指向一个对象代表的句柄或其他与此对象相关的位置)

       简单来说就是通过栈帧中局部变量表所存储的对象引用来对堆内存中的对象实例进行访问或操作的。栈帧中有个对象引用的指针, 通过各种方法指向了堆内存中的对象实例。
而这各种方法中,主要有两种

  1. 通过句柄方式访问
    Java堆中划分出一块内存作为句柄池,reference中存储的是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
    通过句柄方式访问
    优点:
      当对象移动的时候(垃圾回收的时候移动很普遍),这样值需要改变句柄中的指针,但是栈中的指针不需要变化,因为栈中存储的是句柄的地址
    缺点:
      需要进行二次定位,寻找两次指针,开销相对于更大一些
  2. 直接指针访问方式
    reference直接指向了对象类型数据,那么java堆对象分布中就必须考虑如何放置访问类型数据的相关信息,reference存储的直接就时对象地址。
    直接指针访问方式
    优点:速度快,不需要和句柄一样指针定位的开销

参考博客
https://www.cnblogs.com/YYfish/p/6722258.html
https://blog.csdn.net/sinat_34657451/article/details/52038669

猜你喜欢

转载自blog.csdn.net/XlxfyzsFdblj/article/details/83450645