从虚拟机的角度看java对象的创建 - Go语言中文社区

从虚拟机的角度看java对象的创建


一、前言

创建对象有多种方式,最直观的方式就是通过new关键字创建对象。通过new创建的对象存储在java的堆中。

二、对象的创建过程

java创建过程要经过下图的6个步骤,我们所能看到的是new 类名和调用对象的初始化方法,中间的四个是虚拟机内部所执行的,对于开发者来讲它屏蔽了。

将分配的内存初始化为零值就是创建完对象后,基本类型都有默认值,抽象数据类型默认值为null。

Init方法就是代码块,包括构造方法。

三、给对象分配内存 

给对象分配内存有两种指针碰撞和空闲列表两种。

1、指针碰撞

右边为使用内存,左边为空闲内存,两个指针之间为创建所需要的内存,当两个指针碰到后,即对象创建完成。具体过程如下图。

2、空闲列表

Java堆不是规整的,以使用的内存和空闲的内存不是上图中那么有规矩的,而是相互交错的。这个时候就不能使用指针碰撞了。这个时候虚拟机必须维护一个列表,记录哪些内存块是可用的,那么分配的时候可以从表中找出来一块区域给对象的实例并更新在表中。

内存的分配方式是由Java的堆是否规整决定的,而Java的堆是否规整决定是由垃圾回收策略决定的。

四、线程安全问题

在多线程情况下,如果发生线程安全问题有两种解决办法:线程同步和本地线程分配缓冲(TLAB)

1、线程同步

在并发情况下给每个创建线程加锁。

2、本地线程分配缓冲(TLAB)

在堆内存中,给每个线程都分配一个自己的区域,每个线程操作不同的区域,就不会导致线程安全问题。

五对象的结构

对象结构由Header,InstanceData和Padding三部分组成。

1、Header(对象头)

       自身运行时数据 (Mark Word)

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

          运行时数据占的内存根据32位64位虚拟机的不同,分别占32位和64位。

       类型指针

                     对象指向类源数据的指针,这个不是必须需要的。数组这种类型的他还会存储数据的长度。

                     我们是无法操作对象头的。

2、InstanceData(实例数据)

相同宽度的字段分配到一起,比如说long和double会分配到一块,short和char分配到一块。在父类定义的变量会出现在子类之前。

3、Padding(填充)

对齐填充,这部分的数据不是必然存在的,也没有特别含义,相当于占位符。主要是因为HotSpot的自动内存管理系统要求对象起止地址必须是8个字节的整数倍,也就是说对象必须是8个字节的整数倍。而对象头部正好是8个字节的整数倍。因为实例数据如果没有对齐就需要这个填充。

六、对象的访问定位

对象的访问定位有两种,分别是使用句柄和直接指针。HotSpot采用的就是直接指针的方式。

1、使用句柄

引用指向堆中的句柄池,其中句柄池保存了实例对象的地址。

2、直接指针

使用直接指针就是引用类型直接指向真正的对象内存区域

3、存在句柄池为什么还要使用直接指针?

使用句柄池:引用存储的地址一定是固定的,不管堆中对象的内存被垃圾回收,还是移动位置等,引用变量存储的地址是不会变的。变的只是句柄池的地址。

使用直接指针:访问非常快,减少一次寻址的过程,减少性能开销,性能比较高。

使用句柄需要存储到对象实例数据的指针到对象类型数据的指针,如果是使用直接指针,则只需要存储到对象类型数据的指针即可。

 

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/VRival/article/details/83931262
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-08 17:21:43
  • 阅读 ( 989 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢