JVM-对象在JVM中如何创建(对象定位方式/虚拟机创建对象步骤)

版权声明:中华人民共和国持有版权 https://blog.csdn.net/Fly_Fly_Zhang/article/details/89682555

java是一门面向对象的语言,java程序运行过程中无时无刻都有对象被创建出来。在语言层面上,创建对象(克隆,反序列化)就是一个new关键字而已,但是虚拟机层面上却大不相同。

虚拟机创建对象的步骤:

  • 虚拟机遇到一条new指令,首先去检查这个指令的参数能否在运行时常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化。如果没有,那必须先执行类的初始化过程。
  • 类加载检查通过后,虚拟机为新生对象分配内存。对象所需内存大小在类加载完成后便可以完全确定,为对象分配空间就是从java堆中划分出一块确定大小的内存而已。这个地方会有两个问题:
  1. 如果内存是规整的,那么虚拟机将采用的是指针碰撞法 来为对象分配内存。意思是所有用过的内存在一边空闲的内存在另一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是吧指针向空闲那边挪动一段与对象相等的距离。 如果立垃圾收集器选择的是Serial,ParNew这种基于压缩算法的,虚拟机采用的就是这种分配方式。
  2. 如果内存不规整,已使用的内存和未使用的内存相互交错,那么虚拟机将采用的是空闲列表法来为对象分配内存。通俗来说,就是JVM维护了一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的内容 。如果垃圾收集器选择的是CMS这种基于标记-清除算法 的,虚拟机采用这种分配方式。
  3. 还有一个问题就是及时保证new对象时候的安全性 ,因为虚拟机正在给对对象A分配内存,指针还没有来得及修改,对象B又同时使用了原来的指针来分配内存的情况。 虚拟机采用了CAS+失败重试的方式保证更新操作的原子性和TLAB两种方式来解决这个问题。
  • 内存分配结束,虚拟机将分配到的内存空间都初始化为零值(不包括对象头) 。这一步保证了对象的实例字段在java代码中不用赋初值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值(默认值)。
  • 对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的hash值,对象的gc分代年龄等信息,这些信息存在在对象的对象头中。
  • 执行 < init > 方法,把对象按照程序员的意思进行初始化,这样一个对象才算完全生产出来。

对象的定位方式:

建立对象是为了使用对象,java程序需要通过栈上的reference数据来操作堆上的具体对。

Object obj=new  Object();

new Object() 之后其实有两部分内容,一部分是类数据(比如代表类的Class对象),一部分是实例数据
由于reference在JVM规范中只是一个指向对象的引用obj。并没有规定obj应该通过何种方式去定位,访问堆中对象的具体位置,所以对象访问方式也是由虚拟机决定的。

两种主流对象访问方式:

  • 句柄访问: java堆中划分出一块句柄池,obj指向对象的句柄地址,句柄中则包含了类数据地址和实例数据地址。
    优点: reference存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象很常见)时,只会改变句柄中的实例数据指针,而reference本身不需要改变。
    缺点: 增加了一次指针定位的时间开销。
    在这里插入图片描述
  • 指针访问: 对象中存储所有实例数据和类数据的地址,obj指向对象。
    优点: 节省了一次指针定位开销。
    缺点: 在对象被移动时reference本身需要被修改。
    在这里插入图片描述
HotSpot采用后者。

猜你喜欢

转载自blog.csdn.net/Fly_Fly_Zhang/article/details/89682555